// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

//----------------------------------------------------------
// MethodContext.cpp - Primary structure to store all the EE-JIT details required to replay creation of a method
//                     CompileResult contains the stuff generated by a compilation
//----------------------------------------------------------

#include "standardpch.h"
#include "methodcontext.h"
#include "compileresult.h"
#include "lightweightmap.h"
#include "callutils.h"
#include "spmirecordhelper.h"
#include "spmidumphelper.h"
#include "spmiutil.h"

#define sparseMC // Support filling in details where guesses are okay and will still generate good code. (i.e. helper
                 // function addresses)

MethodContext::MethodContext()
{
    methodSize = 0;

#define LWM(map, key, value) map = nullptr;
#include "lwmlist.h"

    cr    = new CompileResult();
    index = -1;
    ignoreStoredConfig = false;
}

MethodContext::~MethodContext()
{
    Destroy();
}

void MethodContext::Destroy()
{
#define LWM(map, key, value)                                                                                           \
    if (map != nullptr)                                                                                                \
        delete map;
#include "lwmlist.h"

    delete cr;
    FreeTempAllocations();
}

#define sparseAddLen(target)                                                                                           \
    if (target != nullptr)                                                                                             \
    {                                                                                                                  \
        if (target->GetCount() != 0)                                                                                   \
            totalLen += target->CalculateArraySize() + 6; /* packet canary from lightweightmap + packet marker */      \
    }

#define sparseWriteFile(target)                                                                                        \
    if (target != nullptr)                                                                                             \
    {                                                                                                                  \
        if (target->GetCount() != 0)                                                                                   \
        {                                                                                                              \
            buff2[buffIndex++] = (unsigned char)Packet_##target;                                                       \
            unsigned int loc   = target->DumpToArray(&buff2[buffIndex + 4]);                                           \
            memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int));                                                     \
            buffIndex += 4 + loc;                                                                                      \
            buff2[buffIndex++] = 0x42;                                                                                 \
        }                                                                                                              \
    }

#define sparseWriteFileCR(target)                                                                                      \
    if (cr != nullptr)                                                                                                 \
    {                                                                                                                  \
        if (cr->target != nullptr)                                                                                     \
        {                                                                                                              \
            if (cr->target->GetCount() != 0)                                                                           \
            {                                                                                                          \
                buff2[buffIndex++] = (unsigned char)PacketCR_##target;                                                 \
                unsigned int loc   = cr->target->DumpToArray(&buff2[buffIndex + 4]);                                   \
                memcpy(&buff2[buffIndex], &loc, sizeof(unsigned int));                                                 \
                buffIndex += 4 + loc;                                                                                  \
                buff2[buffIndex++] = 0x42;                                                                             \
            }                                                                                                          \
        }                                                                                                              \
    }

#define sparseReadFile(target, key, value)                                                                             \
    case Packet_##target:                                                                                              \
    {                                                                                                                  \
        target = new LightWeightMap<key, value>();                                                                     \
        target->ReadFromArray(&buff2[buffIndex], localsize);                                                           \
        break;                                                                                                         \
    }

#define sparseReadFileCR(target, key, value)                                                                           \
    case PacketCR_##target:                                                                                            \
    {                                                                                                                  \
        cr->target = new LightWeightMap<key, value>();                                                                 \
        cr->target->ReadFromArray(&buff2[buffIndex], localsize);                                                       \
        break;                                                                                                         \
    }

#define sparseReadFileDense(target, value)                                                                             \
    case Packet_##target:                                                                                              \
    {                                                                                                                  \
        target = new DenseLightWeightMap<value>();                                                                     \
        target->ReadFromArray(&buff2[buffIndex], localsize);                                                           \
        break;                                                                                                         \
    }

#define sparseReadFileCRDense(target, value)                                                                           \
    case PacketCR_##target:                                                                                            \
    {                                                                                                                  \
        cr->target = new DenseLightWeightMap<value>();                                                                 \
        cr->target->ReadFromArray(&buff2[buffIndex], localsize);                                                       \
        break;                                                                                                         \
    }

unsigned int MethodContext::calculateFileSize()
{
    // Calculate file size
    unsigned int totalLen = 0;

#define LWM(map, key, value) sparseAddLen(map)
#include "lwmlist.h"

    // Compile Result members
    if (cr != nullptr)
    {
#define LWM(map, key, value) sparseAddLen(cr->map);
#include "crlwmlist.h"
    }

    return totalLen;
}

unsigned int MethodContext::calculateRawFileSize()
{
    return 2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + calculateFileSize() +
           2 /* end canary '4', '2' */;
}

unsigned int MethodContext::saveToFile(HANDLE hFile)
{
    unsigned int totalLen = calculateFileSize();
    unsigned int totalFileSize =
        2 /* leading magic cookie 'm', 'c' */ + 4 /* 4-byte data length */ + totalLen + 2 /* end canary '4', '2' */;

    DWORD          bytesWritten = 0;
    unsigned int   buffIndex    = 0;
    unsigned char* buff2        = new unsigned char[totalFileSize];
    buff2[buffIndex++]          = 'm';
    buff2[buffIndex++]          = 'c';
    memcpy(&buff2[buffIndex], &totalLen, sizeof(unsigned int));
    buffIndex += 4;

#define LWM(map, key, value) sparseWriteFile(map)
#include "lwmlist.h"

// Compile Result members
#define LWM(map, key, value) sparseWriteFileCR(map);
#include "crlwmlist.h"

    // Write the end canary
    buff2[buffIndex++] = '4';
    buff2[buffIndex++] = '2';

    Assert(buffIndex == totalFileSize);

    WriteFile(hFile, buff2, totalFileSize, &bytesWritten, NULL);
    delete[] buff2;
    return bytesWritten;
}

// This code can't exist in a function with C++ objects needing destruction. Returns true on success
// (and sets *ppmc with new MethodContext), false on failure.
//
// static
bool MethodContext::Initialize(int mcIndex, unsigned char* buff, DWORD size, /* OUT */ MethodContext** ppmc)
{
    MethodContext* mc = new MethodContext();
    mc->index         = mcIndex;
    *ppmc             = mc;
    return mc->Initialize(mcIndex, buff, size);
}

// static
bool MethodContext::Initialize(int mcIndex, HANDLE hFile, /* OUT */ MethodContext** ppmc)
{
    MethodContext* mc = new MethodContext();
    mc->index         = mcIndex;
    *ppmc             = mc;
    return mc->Initialize(mcIndex, hFile);
}

bool MethodContext::Initialize(int mcIndex, unsigned char* buff, DWORD size)
{
    bool result = true;

    struct Param
    {
        unsigned char* buff;
        DWORD          size;
        MethodContext* pThis;
    } param;
    param.buff  = buff;
    param.size  = size;
    param.pThis = this;

    PAL_TRY(Param*, pParam, &param)
    {
        pParam->pThis->MethodInitHelper(pParam->buff, pParam->size);
    }
    PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
    {
        LogError("Method %d is of low integrity.", mcIndex);
        result = false;
    }
    PAL_ENDTRY

    return result;
}

bool MethodContext::Initialize(int mcIndex, HANDLE hFile)
{
    bool result = true;

    struct Param
    {
        HANDLE         hFile;
        MethodContext* pThis;
    } param;
    param.hFile = hFile;
    param.pThis = this;

    PAL_TRY(Param*, pParam, &param)
    {
        pParam->pThis->MethodInitHelperFile(pParam->hFile);
    }
    PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchMC)
    {
        LogError("Method %d is of low integrity.", mcIndex);
        result = false;
    }
    PAL_ENDTRY

    return result;
}

void MethodContext::MethodInitHelperFile(HANDLE hFile)
{
    DWORD        bytesRead;
    char         buff[512];
    unsigned int totalLen = 0;

    AssertCode(ReadFile(hFile, buff, 2 + sizeof(unsigned int), &bytesRead, NULL) == TRUE,
               EXCEPTIONCODE_MC); // Read Magic number and totalLen
    AssertCodeMsg((buff[0] == 'm') && (buff[1] == 'c'), EXCEPTIONCODE_MC, "Didn't find magic number");
    memcpy(&totalLen, &buff[2], sizeof(unsigned int));
    unsigned char* buff2 = new unsigned char[totalLen + 2]; // total + End Canary
    AssertCode(ReadFile(hFile, buff2, totalLen + 2, &bytesRead, NULL) == TRUE, EXCEPTIONCODE_MC);
    AssertCodeMsg((buff2[totalLen] == '4') && (buff2[totalLen + 1] == '2'), EXCEPTIONCODE_MC, "Didn't find end canary");
    MethodInitHelper(buff2, totalLen);
}

void MethodContext::MethodInitHelper(unsigned char* buff2, unsigned int totalLen)
{
    unsigned int   buffIndex = 0;
    unsigned int   localsize = 0;
    unsigned char  canary    = 0xff;
    unsigned char* buff3     = nullptr;

    FreeTempAllocations();

    while (buffIndex < totalLen)
    {
        mcPackets packetType = (mcPackets)buff2[buffIndex++];
        memcpy(&localsize, &buff2[buffIndex], sizeof(unsigned int));
        buffIndex += sizeof(unsigned int);

        switch (packetType)
        {
#define LWM(map, key, value) sparseReadFile(map, key, value)
#define DENSELWM(map, value) sparseReadFileDense(map, value)
#include "lwmlist.h"

#define LWM(map, key, value) sparseReadFileCR(map, key, value)
#define DENSELWM(map, value) sparseReadFileCRDense(map, value)
#include "crlwmlist.h"

            default:
                LogException(EXCEPTIONCODE_MC, "Read ran into unknown packet type %u. Are you using a newer recorder?",
                             packetType);
                // break;
        }
        buffIndex += localsize;
        canary = buff2[buffIndex++];
        AssertCodeMsg(canary == 0x42, EXCEPTIONCODE_MC, "Didn't find trailing canary for map");
    }
    AssertCodeMsg((buff2[buffIndex++] == '4') && (buff2[buffIndex++] == '2'), EXCEPTIONCODE_MC,
                  "Didn't find trailing canary for map");
    delete[] buff2;
}

#define dumpStat(target)                                                                                               \
    if (target != nullptr)                                                                                             \
    {                                                                                                                  \
        if (target->GetCount() > 0)                                                                                    \
        {                                                                                                              \
            int t = sprintf_s(buff, len, "%u", target->GetCount());                                                    \
            buff += t;                                                                                                 \
            len -= t;                                                                                                  \
        }                                                                                                              \
    }                                                                                                                  \
    {                                                                                                                  \
        *buff++ = ',';                                                                                                 \
        len--;                                                                                                         \
    }                                                                                                                  \
    if (target != nullptr)                                                                                             \
    {                                                                                                                  \
        int t = sprintf_s(buff, len, "%u", target->CalculateArraySize());                                              \
        buff += t;                                                                                                     \
        len -= t;                                                                                                      \
    }                                                                                                                  \
    {                                                                                                                  \
        *buff++ = ',';                                                                                                 \
        len--;                                                                                                         \
    }

// Dump statistics about each LightWeightMap to the buffer: count of elements, and total size in bytes of map.
int MethodContext::dumpStatToBuffer(char* buff, int len)
{
    char* obuff = buff;
// assumption of enough buffer.. :-|

#define LWM(map, key, value) dumpStat(map)
#include "lwmlist.h"

// Compile Result members
#define LWM(map, key, value) dumpStat(cr->map);
#include "crlwmlist.h"

    return (int)(buff - obuff);
}
int MethodContext::dumpStatTitleToBuffer(char* buff, int len)
{
    const char* title =

#define LWM(map, key, value) #map "," #map " SZ,"
#include "lwmlist.h"

#define LWM(map, key, value) "CR_" #map ",CR_" #map " SZ,"
#include "crlwmlist.h"

        ;

    int titleLen = (int)strlen(title);
    if ((titleLen + 1) > len)
    {
        LogError("titleLen is larger than given len");
        return 0;
    }
    strcpy_s(buff, len, title);
    return titleLen;
}

#define softMapEqual(a)                                                                                                \
    if (a != nullptr)                                                                                                  \
    {                                                                                                                  \
        if (other->a == nullptr)                                                                                       \
            return false;                                                                                              \
        if (a->GetCount() != other->a->GetCount())                                                                     \
            return false;                                                                                              \
    }                                                                                                                  \
    else if (other->a != nullptr)                                                                                      \
        return false;

bool MethodContext::Equal(MethodContext* other)
{
    // returns true if equal.  Note this is permissive, that is to say that we may reason that too many things are
    // equal. Adding more detailed checks would cause us to reason more things as unique;

    // Compare MethodInfo's first.
    CORINFO_METHOD_INFO otherInfo;
    unsigned            otherFlags = 0;
    CORINFO_OS          otherOs    = CORINFO_WINNT;
    other->repCompileMethod(&otherInfo, &otherFlags, &otherOs);

    CORINFO_METHOD_INFO ourInfo;
    unsigned            ourFlags = 0;
    CORINFO_OS          ourOs    = CORINFO_WINNT;
    repCompileMethod(&ourInfo, &ourFlags, &ourOs);

    if (otherInfo.ILCodeSize != ourInfo.ILCodeSize)
        return false;
    if (otherInfo.args.numArgs != ourInfo.args.numArgs)
        return false;
    if (otherInfo.args.retType != ourInfo.args.retType)
        return false;
    if (otherInfo.locals.numArgs != ourInfo.locals.numArgs)
        return false;
    if (otherInfo.EHcount != ourInfo.EHcount)
        return false;
    if (otherInfo.options != ourInfo.options)
        return false;
    for (unsigned int j = 0; j < otherInfo.ILCodeSize; j++)
        if (otherInfo.ILCode[j] != ourInfo.ILCode[j])
            return false;
    if (otherInfo.maxStack != ourInfo.maxStack)
        return false;
    if (otherInfo.regionKind != ourInfo.regionKind)
        return false;
    if (otherInfo.args.callConv != ourInfo.args.callConv)
        return false;
    if (otherInfo.args.cbSig != ourInfo.args.cbSig)
        return false;
    if (otherInfo.args.flags != ourInfo.args.flags)
        return false;
    if (otherInfo.locals.cbSig != ourInfo.locals.cbSig)
        return false;
    if (otherFlags != ourFlags)
        return false;
    if (otherOs != ourOs)
        return false;

// Now compare the other maps to "estimate" equality.

#define LWM(map, key, value) softMapEqual(map)
#include "lwmlist.h"

// Compile Result members
#define LWM(map, key, value) softMapEqual(cr->map)
#include "crlwmlist.h"

    // Base case is we "match"
    return true;
}

//------------------------------------------------------------------------------
// MethodContext::recGlobalContext
//    This method copies any relevant global (i.e. per-JIT-instance) data from
//    the given method context. Currently this is limited to configuration
//    values, but may grow to encompass other information in the future (e.g.
//    any information that is exposed by the ICorJitHost interface and is
//    therefore accessible outside the context of a call to
//    `ICJI::compileMethod`).
//
//    This method is intended to be called as part of initializing a method
//    during collection.
void MethodContext::recGlobalContext(const MethodContext& other)
{
    Assert(GetIntConfigValue == nullptr);
    Assert(GetStringConfigValue == nullptr);

    if (other.GetIntConfigValue != nullptr)
    {
        GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>(*other.GetIntConfigValue);
    }

    if (other.GetStringConfigValue != nullptr)
    {
        GetStringConfigValue = new LightWeightMap<DWORD, DWORD>(*other.GetStringConfigValue);
    }
}

// dumpToConsole: Display the method context numbered `mcNumber` to the console. If `simple` is true,
// dump without function name information. This is useful to debug problems with the creation of that
// information, which requires looking at the dumped info.
void MethodContext::dumpToConsole(int mcNumber, bool simple)
{
    printf("*****************************************");
    if (mcNumber != -1)
    {
        printf(" method context #%d", mcNumber);
    }

    if (!simple)
    {
        // Dump method name, etc., to output.
        char bufferIdentityInfo[METHOD_IDENTITY_INFO_SIZE];
        int cbLen = dumpMethodIdentityInfoToBuffer(bufferIdentityInfo, METHOD_IDENTITY_INFO_SIZE);
        if (cbLen >= 0)
        {
            printf(" %s", bufferIdentityInfo);
        }
    }

    printf("\n");

#define LWM(map, key, value) dumpLWM(this, map)
#define DENSELWM(map, value) dumpLWMDense(this, map)
#include "lwmlist.h"

// Compile Result members
#define LWM(map, key, value) dumpLWM(this->cr, map)
#define DENSELWM(map, value) dumpLWMDense(this->cr, map)
#include "crlwmlist.h"
}

const char* toString(InfoAccessType iat)
{
    switch (iat)
    {
    case IAT_VALUE:
        return "VALUE";
    case IAT_PVALUE:
        return "PVALUE";
    case IAT_PPVALUE:
        return "PPVALUE";
    case IAT_RELPVALUE:
        return "RELPVALUE";
    default:
        return "UNKNOWN";
    }
};

const char* toString(CORINFO_CALL_KIND cick)
{
    switch (cick)
    {
    case CORINFO_CALL:
        return "CALL";
    case CORINFO_CALL_CODE_POINTER:
        return "CALL_CODE_POINTER";
    case CORINFO_VIRTUALCALL_STUB:
        return "VIRTUALCALL_STUB";
    case CORINFO_VIRTUALCALL_LDVIRTFTN:
        return "VIRTUALCALL_LDVIRTFTN";
    case CORINFO_VIRTUALCALL_VTABLE:
        return "VIRTUALCALL_VTABLE";
    default:
        return "UNKNOWN";
    }
};

const char* toString(CorInfoType cit)
{
    switch (cit)
    {
        case CORINFO_TYPE_UNDEF:
            return "undef";
        case CORINFO_TYPE_VOID:
            return "void";
        case CORINFO_TYPE_BOOL:
            return "bool";
        case CORINFO_TYPE_CHAR:
            return "char";
        case CORINFO_TYPE_BYTE:
            return "byte";
        case CORINFO_TYPE_UBYTE:
            return "ubyte";
        case CORINFO_TYPE_SHORT:
            return "short";
        case CORINFO_TYPE_USHORT:
            return "ushort";
        case CORINFO_TYPE_INT:
            return "int";
        case CORINFO_TYPE_UINT:
            return "uint";
        case CORINFO_TYPE_LONG:
            return "long";
        case CORINFO_TYPE_ULONG:
            return "ulong";
        case CORINFO_TYPE_NATIVEINT:
            return "nativeint";
        case CORINFO_TYPE_NATIVEUINT:
            return "nativeuint";
        case CORINFO_TYPE_FLOAT:
            return "float";
        case CORINFO_TYPE_DOUBLE:
            return "double";
        case CORINFO_TYPE_STRING:
            return "string";
        case CORINFO_TYPE_PTR:
            return "ptr";
        case CORINFO_TYPE_BYREF:
            return "byref";
        case CORINFO_TYPE_VALUECLASS:
            return "valueclass";
        case CORINFO_TYPE_CLASS:
            return "class";
        case CORINFO_TYPE_REFANY:
            return "refany";
        case CORINFO_TYPE_VAR:
            return "var";
        default:
            return "UNKNOWN";
    }
}

const char* toString(CorInfoTypeWithMod cit)
{
    // Need to cast `cit` to numeric type to avoid clang compiler warnings
    // "case value not in enumerated type 'CorInfoTypeWithMod'".
    switch ((unsigned)cit)
    {
        case (unsigned)(CORINFO_TYPE_BYREF | CORINFO_TYPE_MOD_PINNED):
            return "pinned byref";
        case (unsigned)(CORINFO_TYPE_CLASS | CORINFO_TYPE_MOD_PINNED):
            return "pinned class";
        default:
            return toString((CorInfoType)(cit & CORINFO_TYPE_MASK));
    }
}

unsigned int toCorInfoSize(CorInfoType cit)
{
    switch (cit)
    {
        case CORINFO_TYPE_BOOL:
        case CORINFO_TYPE_BYTE:
        case CORINFO_TYPE_UBYTE:
            return 1;

        case CORINFO_TYPE_CHAR:
        case CORINFO_TYPE_SHORT:
        case CORINFO_TYPE_USHORT:
            return 2;

        case CORINFO_TYPE_FLOAT:
        case CORINFO_TYPE_INT:
        case CORINFO_TYPE_UINT:
            return 4;

        case CORINFO_TYPE_DOUBLE:
        case CORINFO_TYPE_LONG:
        case CORINFO_TYPE_ULONG:
            return 8;

        case CORINFO_TYPE_NATIVEINT:
        case CORINFO_TYPE_NATIVEUINT:
        case CORINFO_TYPE_PTR:
        case CORINFO_TYPE_BYREF:
        case CORINFO_TYPE_CLASS:
            return (int)SpmiTargetPointerSize();

        case CORINFO_TYPE_STRING:
        case CORINFO_TYPE_VALUECLASS:
        case CORINFO_TYPE_REFANY:
        case CORINFO_TYPE_UNDEF:
        case CORINFO_TYPE_VOID:
        default:
            __debugbreak();
            return 0;
    }
    return -1;
}

void MethodContext::recCompileMethod(CORINFO_METHOD_INFO* info, unsigned flags, CORINFO_OS os)
{
    if (CompileMethod == nullptr)
        CompileMethod = new LightWeightMap<DWORD, Agnostic_CompileMethod>();

    Agnostic_CompileMethod value;

    value.info.ftn           = CastHandle(info->ftn);
    value.info.scope         = CastHandle(info->scope);
    value.info.ILCode_offset = (DWORD)CompileMethod->AddBuffer(info->ILCode, info->ILCodeSize);
    value.info.ILCodeSize    = (DWORD)info->ILCodeSize;
    value.info.maxStack      = (DWORD)info->maxStack;
    value.info.EHcount       = (DWORD)info->EHcount;
    value.info.options       = (DWORD)info->options;
    value.info.regionKind    = (DWORD)info->regionKind;

    value.info.args   = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(info->args, CompileMethod, SigInstHandleMap);
    value.info.locals = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(info->locals, CompileMethod, SigInstHandleMap);

    value.os = (DWORD)os;

    value.flags = (DWORD)flags;

    CompileMethod->Add(0, value);
    DEBUG_REC(dmpCompileMethod(0, value));
}
void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value)
{
    printf("CompileMethod key %u, value ftn-%016" PRIX64 " scp-%016" PRIX64 " ilo-%u ils-%u ms-%u ehc-%u opt-%u rk-%u args-%s locals-%s flg-%08X os-%u",
           key, value.info.ftn, value.info.scope, value.info.ILCode_offset, value.info.ILCodeSize, value.info.maxStack,
           value.info.EHcount, value.info.options, value.info.regionKind,
           SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.info.args, CompileMethod, SigInstHandleMap).c_str(),
           SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.info.locals, CompileMethod, SigInstHandleMap).c_str(),
           value.flags, value.os);
}
void MethodContext::repCompileMethod(CORINFO_METHOD_INFO* info, unsigned* flags, CORINFO_OS* os)
{
    Agnostic_CompileMethod value;
    value = LookupByKeyOrMissNoMessage(CompileMethod, 0); // The only item in this set is a single group of inputs to CompileMethod

    DEBUG_REP(dmpCompileMethod(0, value));

    info->ftn        = (CORINFO_METHOD_HANDLE)value.info.ftn;
    info->scope      = (CORINFO_MODULE_HANDLE)value.info.scope;
    info->ILCode     = CompileMethod->GetBuffer(value.info.ILCode_offset);
    info->ILCodeSize = (unsigned)value.info.ILCodeSize;
    methodSize       = info->ILCodeSize;
    info->maxStack   = (unsigned)value.info.maxStack;
    info->EHcount    = (unsigned)value.info.EHcount;
    info->options    = (CorInfoOptions)value.info.options;
    info->regionKind = (CorInfoRegionKind)value.info.regionKind;

    info->args   = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.info.args, CompileMethod, SigInstHandleMap);
    info->locals = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.info.locals, CompileMethod, SigInstHandleMap);

    *flags             = (unsigned)value.flags;
    *os                = (CORINFO_OS)value.os;
}

void MethodContext::recGetMethodClass(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CLASS_HANDLE classHandle)
{
    if (GetMethodClass == nullptr)
        GetMethodClass = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(methodHandle);
    DWORDLONG value = CastHandle(classHandle);
    GetMethodClass->Add(key, value);
    DEBUG_REC(dmpGetMethodClass(key, value));
}
void MethodContext::dmpGetMethodClass(DWORDLONG key, DWORDLONG value)
{
    printf("GetMethodClass key %016" PRIX64 ", value %016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetMethodClass(CORINFO_METHOD_HANDLE methodHandle)
{
    DWORDLONG key = CastHandle(methodHandle);
    DWORDLONG value = LookupByKeyOrMiss(GetMethodClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetMethodClass(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetClassAttribs(CORINFO_CLASS_HANDLE classHandle, DWORD attribs)
{
    if (GetClassAttribs == nullptr)
        GetClassAttribs = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(classHandle);
    GetClassAttribs->Add(key, attribs);
    DEBUG_REC(dmpGetClassAttribs(key, attribs));
}
void MethodContext::dmpGetClassAttribs(DWORDLONG key, DWORD value)
{
    printf("GetClassAttribs key %016" PRIX64 ", value %08X (%s)", key, value, SpmiDumpHelper::DumpCorInfoFlag((CorInfoFlag)value).c_str());
}
DWORD MethodContext::repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle)
{
    DWORDLONG key = CastHandle(classHandle);
    DWORD value = LookupByKeyOrMiss(GetClassAttribs, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetClassAttribs(key, value));
    return value;
}

void MethodContext::recIsIntrinsic(CORINFO_METHOD_HANDLE ftn, bool result)
{
    if (IsIntrinsic == nullptr)
        IsIntrinsic = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(ftn);
    DWORD value = result ? 1 : 0;
    IsIntrinsic->Add(key, value);
    DEBUG_REC(dmpIsIntrinsic(key, value));
}
void MethodContext::dmpIsIntrinsic(DWORDLONG key, DWORD value)
{
    printf("IsIntrinsic key ftn-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repIsIntrinsic(CORINFO_METHOD_HANDLE ftn)
{
    DWORDLONG key = CastHandle(ftn);
    DWORD value = LookupByKeyOrMiss(IsIntrinsic, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsIntrinsic(key, value));
    return value != 0;
}

void MethodContext::recNotifyMethodInfoUsage(CORINFO_METHOD_HANDLE ftn, bool result)
{
    if (NotifyMethodInfoUsage == nullptr)
        NotifyMethodInfoUsage = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(ftn);
    DWORD value = result ? 1 : 0;
    NotifyMethodInfoUsage->Add(key, value);
    DEBUG_REC(dmpNotifyMethodInfoUsage(key, value));
}
void MethodContext::dmpNotifyMethodInfoUsage(DWORDLONG key, DWORD value)
{
    printf("NotifyMethodInfoUsage key ftn-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repNotifyMethodInfoUsage(CORINFO_METHOD_HANDLE ftn)
{
    DWORDLONG key = CastHandle(ftn);
    DWORD value = LookupByKeyOrMiss(NotifyMethodInfoUsage, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpNotifyMethodInfoUsage(key, value));
    return value != 0;
}

void MethodContext::recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs)
{
    if (GetMethodAttribs == nullptr)
        GetMethodAttribs = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(methodHandle);
    GetMethodAttribs->Add(key, attribs);
    DEBUG_REC(dmpGetMethodAttribs(key, attribs));
}
void MethodContext::dmpGetMethodAttribs(DWORDLONG key, DWORD value)
{
    printf("GetMethodAttribs key %016" PRIX64 ", value %08X (%s)", key, value, SpmiDumpHelper::DumpCorInfoFlag((CorInfoFlag)value).c_str());
}
DWORD MethodContext::repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle)
{
    DWORDLONG key = CastHandle(methodHandle);
    DWORD value = LookupByKeyOrMiss(GetMethodAttribs, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetMethodAttribs(key, value));

    if ((cr->repSetMethodAttribs(methodHandle) & CORINFO_FLG_BAD_INLINEE) != 0)
        value |= CORINFO_FLG_DONT_INLINE;
    return value;
}

void MethodContext::recGetClassModule(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE mod)
{
    if (GetClassModule == nullptr)
        GetClassModule = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = CastHandle(mod);
    GetClassModule->Add(key, value);
    DEBUG_REC(dmpGetClassModule(key, value));
}
void MethodContext::dmpGetClassModule(DWORDLONG key, DWORDLONG value)
{
    printf("GetClassModule cls-%016" PRIX64 ", mod-%016" PRIX64 "", key, value);
}
CORINFO_MODULE_HANDLE MethodContext::repGetClassModule(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetClassModule, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetClassModule(key, value));
    CORINFO_MODULE_HANDLE result = (CORINFO_MODULE_HANDLE)value;
    return result;
}

void MethodContext::recGetModuleAssembly(CORINFO_MODULE_HANDLE mod, CORINFO_ASSEMBLY_HANDLE assem)
{
    if (GetModuleAssembly == nullptr)
        GetModuleAssembly = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(mod);
    DWORDLONG value = CastHandle(assem);
    GetModuleAssembly->Add(key, value);
    DEBUG_REC(dmpGetModuleAssembly(key, value));
}
void MethodContext::dmpGetModuleAssembly(DWORDLONG key, DWORDLONG value)
{
    printf("GetModuleAssembly mod-%016" PRIX64 ", assem-%016" PRIX64 "", key, value);
}
CORINFO_ASSEMBLY_HANDLE MethodContext::repGetModuleAssembly(CORINFO_MODULE_HANDLE mod)
{
    DWORDLONG key = CastHandle(mod);
    DWORDLONG value = LookupByKeyOrMiss(GetModuleAssembly, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetModuleAssembly(key, value));
    CORINFO_ASSEMBLY_HANDLE result = (CORINFO_ASSEMBLY_HANDLE)value;
    return result;
}

void MethodContext::recGetAssemblyName(CORINFO_ASSEMBLY_HANDLE assem, const char* assemblyName)
{
    if (GetAssemblyName == nullptr)
        GetAssemblyName = new LightWeightMap<DWORDLONG, DWORD>();

    DWORD value;
    if (assemblyName != nullptr)
    {
        value = GetAssemblyName->AddBuffer((const unsigned char*)assemblyName, (DWORD)strlen(assemblyName) + 1);
    }
    else
    {
        value = (DWORD)-1;
    }

    DWORDLONG key = CastHandle(assem);
    GetAssemblyName->Add(key, value);
    DEBUG_REC(dmpGetAssemblyName(key, value));
}
void MethodContext::dmpGetAssemblyName(DWORDLONG key, DWORD value)
{
    const char* assemblyName = (const char*)GetAssemblyName->GetBuffer(value);
    printf("GetAssemblyName assem-%016" PRIX64 ", value-%u '%s'", key, value, assemblyName);
    GetAssemblyName->Unlock();
}
const char* MethodContext::repGetAssemblyName(CORINFO_ASSEMBLY_HANDLE assem)
{
    DWORDLONG key = CastHandle(assem);
    const char* result = "hackishAssemblyName";
    DWORD value = (DWORD)-1;
    int itemIndex = -1;
    if (GetAssemblyName != nullptr)
    {
        itemIndex = GetAssemblyName->GetIndex(key);
    }
    if (itemIndex >= 0)
    {
        value = GetAssemblyName->Get(key);
        result = (const char*)GetAssemblyName->GetBuffer(value);
    }
    DEBUG_REP(dmpGetAssemblyName(key, value));
    return result;
}

// Note - the jit will call freearray on the array we give back....
void MethodContext::recGetVars(CORINFO_METHOD_HANDLE      ftn,
                               ULONG32*                   cVars,
                               ICorDebugInfo::ILVarInfo** vars_in,
                               bool*                      extendOthers)
{
    if (GetVars == nullptr)
        GetVars = new LightWeightMap<DWORDLONG, Agnostic_GetVars>();

    Agnostic_GetVars value;
    value.cVars = (DWORD)*cVars;
    value.vars_offset =
        (DWORD)GetVars->AddBuffer((unsigned char*)*vars_in, sizeof(ICorDebugInfo::ILVarInfo) * (*cVars));
    value.extendOthers = (DWORD)*extendOthers;

    DWORDLONG key = CastHandle(ftn);
    GetVars->Add(key, value);
    DEBUG_REC(dmpGetVars(key, value));
}
void MethodContext::dmpGetVars(DWORDLONG key, const Agnostic_GetVars& value)
{
    ICorDebugInfo::ILVarInfo* vars = (ICorDebugInfo::ILVarInfo*)GetVars->GetBuffer(value.vars_offset);
    printf("GetVars key ftn-%016" PRIX64 ", value cVars-%u extendOthers-%u (", key, value.cVars, value.extendOthers);
    for (unsigned int i = 0; i < value.cVars; i++)
        printf("(%u %u %u %u)", i, vars[i].startOffset, vars[i].endOffset, vars[i].varNumber);
    printf(")");
    GetVars->Unlock();
}
void MethodContext::repGetVars(CORINFO_METHOD_HANDLE      ftn,
                               ULONG32*                   cVars,
                               ICorDebugInfo::ILVarInfo** vars_in,
                               bool*                      extendOthers)
{
    if (GetVars == nullptr)
    {
        *cVars = 0;
        return;
    }

    DWORDLONG key = CastHandle(ftn);
    Agnostic_GetVars value = GetVars->Get(key);
    DEBUG_REP(dmpGetVars(key, value));

    *cVars = (ULONG32)value.cVars;
    if (*cVars > 0)
        *vars_in  = (ICorDebugInfo::ILVarInfo*)GetVars->GetBuffer(value.vars_offset);
    *extendOthers = value.extendOthers != 0;
}

// Note - the jit will call freearray on the array we give back....
void MethodContext::recGetBoundaries(CORINFO_METHOD_HANDLE         ftn,
                                     unsigned int*                 cILOffsets,
                                     uint32_t**                    pILOffsets,
                                     ICorDebugInfo::BoundaryTypes* implicitBoundaries)
{
    if (GetBoundaries == nullptr)
        GetBoundaries = new LightWeightMap<DWORDLONG, Agnostic_GetBoundaries>();

    Agnostic_GetBoundaries value;

    value.cILOffsets = (DWORD)*cILOffsets;
    value.pILOffset_offset =
        (DWORD)GetBoundaries->AddBuffer((unsigned char*)*pILOffsets, sizeof(DWORD) * (*cILOffsets));
    value.implicitBoundaries = *implicitBoundaries;

    DWORDLONG key = CastHandle(ftn);
    GetBoundaries->Add(key, value);
    DEBUG_REC(dmpGetBoundaries(key, value));
}
void MethodContext::dmpGetBoundaries(DWORDLONG key, const Agnostic_GetBoundaries& value)
{
    printf("GetBoundaries key ftn-%016" PRIX64 ", value cnt-%u imp-%u{", key, value.cILOffsets, value.implicitBoundaries);
    DWORD* bnd = (DWORD*)GetBoundaries->GetBuffer(value.pILOffset_offset);
    for (unsigned int i = 0; i < value.cILOffsets; i++)
    {
        printf("%u", bnd[i]);
        if (i < (value.cILOffsets + 1))
            printf(",");
    }
    GetBoundaries->Unlock();
    printf("}");
}
void MethodContext::repGetBoundaries(CORINFO_METHOD_HANDLE         ftn,
                                     unsigned int*                 cILOffsets,
                                     uint32_t**                    pILOffsets,
                                     ICorDebugInfo::BoundaryTypes* implicitBoundaries)
{
    DWORDLONG key = CastHandle(ftn);
    Agnostic_GetBoundaries value = LookupByKeyOrMiss(GetBoundaries, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetBoundaries(key, value));

    *cILOffsets = (unsigned int)value.cILOffsets;
    if (*cILOffsets > 0)
        *pILOffsets    = (uint32_t*)GetBoundaries->GetBuffer(value.pILOffset_offset);
    *implicitBoundaries = (ICorDebugInfo::BoundaryTypes)value.implicitBoundaries;
}

void MethodContext::recInitClass(CORINFO_FIELD_HANDLE   field,
                                 CORINFO_METHOD_HANDLE  method,
                                 CORINFO_CONTEXT_HANDLE context,
                                 CorInfoInitClassResult result)
{
    if (InitClass == nullptr)
        InitClass = new LightWeightMap<Agnostic_InitClass, DWORD>();

    Agnostic_InitClass key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.field   = CastHandle(field);
    key.method  = CastHandle(method);
    key.context = CastHandle(context);

    DWORD value = (DWORD)result;
    InitClass->Add(key, value);
    DEBUG_REC(dmpInitClass(key, value));
}
void MethodContext::dmpInitClass(const Agnostic_InitClass& key, DWORD value)
{
    printf("InitClass key fld-%016" PRIX64 " meth-%016" PRIX64 " con-%016" PRIX64 ", value res-%u", key.field, key.method,
           key.context, value);
}
CorInfoInitClassResult MethodContext::repInitClass(CORINFO_FIELD_HANDLE   field,
                                                   CORINFO_METHOD_HANDLE  method,
                                                   CORINFO_CONTEXT_HANDLE context)
{
    Agnostic_InitClass key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.field   = CastHandle(field);
    key.method  = CastHandle(method);
    key.context = CastHandle(context);

    DWORD value = InitClass->Get(key);
    DEBUG_REP(dmpInitClass(key, value));
    CorInfoInitClassResult result = (CorInfoInitClassResult)value;
    return result;
}

void MethodContext::recGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn,
                                                 char*                 methodName,
                                                 const char**          className,
                                                 const char**          namespaceName,
                                                 const char**          enclosingClassName)
{
    if (GetMethodNameFromMetadata == nullptr)
        GetMethodNameFromMetadata = new LightWeightMap<Agnostic_CORINFO_METHODNAME_TOKENin, Agnostic_CORINFO_METHODNAME_TOKENout>();
    Agnostic_CORINFO_METHODNAME_TOKENout  value;
    Agnostic_CORINFO_METHODNAME_TOKENin key;
    key.ftn = CastHandle(ftn);
    key.className = (className != nullptr);
    key.namespaceName = (namespaceName != nullptr);
    key.enclosingClassName = (enclosingClassName != nullptr);

    if (methodName != nullptr)
        value.methodName = GetMethodNameFromMetadata->AddBuffer((unsigned char*)methodName, (DWORD)strlen(methodName) + 1);
    else
        value.methodName = (DWORD)-1;

    if ((className != nullptr) && (*className != nullptr))
        value.className = GetMethodNameFromMetadata->AddBuffer((unsigned char*)*className, (DWORD)strlen(*className) + 1);
    else
        value.className = (DWORD)-1;

    if ((namespaceName != nullptr) && (*namespaceName != nullptr))
        value.namespaceName =
            GetMethodNameFromMetadata->AddBuffer((unsigned char*)*namespaceName, (DWORD)strlen(*namespaceName) + 1);
    else
        value.namespaceName = (DWORD)-1;

    if ((enclosingClassName != nullptr) && (*enclosingClassName != nullptr))
        value.enclosingClassName =
            GetMethodNameFromMetadata->AddBuffer((unsigned char*)*enclosingClassName, (DWORD)strlen(*enclosingClassName) + 1);
    else
        value.enclosingClassName = (DWORD)-1;

    GetMethodNameFromMetadata->Add(key, value);
    DEBUG_REC(dmpGetMethodNameFromMetadata(key, value));
}

void MethodContext::dmpGetMethodNameFromMetadata(Agnostic_CORINFO_METHODNAME_TOKENin key, Agnostic_CORINFO_METHODNAME_TOKENout value)
{
    unsigned char* methodName    = (unsigned char*)GetMethodNameFromMetadata->GetBuffer(value.methodName);
    unsigned char* className     = (unsigned char*)GetMethodNameFromMetadata->GetBuffer(value.className);
    unsigned char* namespaceName = (unsigned char*)GetMethodNameFromMetadata->GetBuffer(value.namespaceName);
    unsigned char* enclosingClassName = (unsigned char*)GetMethodNameFromMetadata->GetBuffer(value.enclosingClassName);
    printf("GetMethodNameFromMetadata key - ftn-%016" PRIX64 " classNonNull-%u namespaceNonNull-%u enclosingClassNonNull-%u, value meth-'%s', "
           "class-'%s', namespace-'%s' enclosingClass-'%s'",
           key.ftn, key.className, key.namespaceName, key.enclosingClassName, methodName, className, namespaceName, enclosingClassName);
    GetMethodNameFromMetadata->Unlock();
}

const char* MethodContext::repGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn,
                                                        const char**          moduleName,
                                                        const char**          namespaceName,
                                                        const char**          enclosingClassName)
{
    Agnostic_CORINFO_METHODNAME_TOKENin key;

    key.ftn = CastHandle(ftn);
    key.className = (moduleName != nullptr);
    key.namespaceName = (namespaceName != nullptr);
    key.enclosingClassName = (enclosingClassName != nullptr);

    Agnostic_CORINFO_METHODNAME_TOKENout value = LookupByKeyOrMiss(
        GetMethodNameFromMetadata,
        key,
        ": ftn-%016" PRIX64 " className-%u namespaceName-%u enclosingClassName-%u",
        key.ftn,
        key.className,
        key.namespaceName,
        key.enclosingClassName);

    DEBUG_REP(dmpGetMethodNameFromMetadata(key, value));

    const char* result = (const char*)GetMethodNameFromMetadata->GetBuffer(value.methodName);

    if (moduleName != nullptr)
    {
        *moduleName = (const char*)GetMethodNameFromMetadata->GetBuffer(value.className);
    }

    if (namespaceName != nullptr)
    {
        *namespaceName = (const char*)GetMethodNameFromMetadata->GetBuffer(value.namespaceName);
    }

    if (enclosingClassName != nullptr)
    {
        *enclosingClassName = (const char*)GetMethodNameFromMetadata->GetBuffer(value.enclosingClassName);
    }

    return result;
}

void MethodContext::recGetJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes, DWORD result)
{
    if (GetJitFlags == nullptr)
        GetJitFlags = new LightWeightMap<DWORD, DD>();

    DD value;
    value.A = (DWORD)GetJitFlags->AddBuffer((unsigned char*)jitFlags, sizeInBytes);
    value.B = result;

    // NOTE: getJitFlags() is expected to be idempotent per method, so the mapping key is always
    //       zero.
    GetJitFlags->Add(0, value);
    DEBUG_REC(dmpGetJitFlags(0, value));
}

// TODO: we need a widely-available utility function that can print a CORJIT_FLAGS::CorJitFlag.
// Maybe something in corjitflags.h itself.
const char* CorJitFlagToString(CORJIT_FLAGS::CorJitFlag flag)
{
    switch (flag)
    {
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_SPEED_OPT:
        return "CORJIT_FLAG_SPEED_OPT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_SIZE_OPT:
        return "CORJIT_FLAG_SIZE_OPT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_DEBUG_CODE:
        return "CORJIT_FLAG_DEBUG_CODE";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_DEBUG_EnC:
        return "CORJIT_FLAG_DEBUG_EnC";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_DEBUG_INFO:
        return "CORJIT_FLAG_DEBUG_INFO";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_MIN_OPT:
        return "CORJIT_FLAG_MIN_OPT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_ENABLE_CFG:
        return "CORJIT_FLAG_ENABLE_CFG";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_OSR:
        return "CORJIT_FLAG_OSR";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_ALT_JIT:
        return "CORJIT_FLAG_ALT_JIT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_FROZEN_ALLOC_ALLOWED:
        return "CORJIT_FLAG_FROZEN_ALLOC_ALLOWED";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_MAKEFINALCODE:
        return "CORJIT_FLAG_MAKEFINALCODE";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_READYTORUN:
        return "CORJIT_FLAG_READYTORUN";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_PROF_ENTERLEAVE:
        return "CORJIT_FLAG_PROF_ENTERLEAVE";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_PROF_NO_PINVOKE_INLINE:
        return "CORJIT_FLAG_PROF_NO_PINVOKE_INLINE";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_PREJIT:
        return "CORJIT_FLAG_PREJIT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_RELOC:
        return "CORJIT_FLAG_RELOC";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_IL_STUB:
        return "CORJIT_FLAG_IL_STUB";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_PROCSPLIT:
        return "CORJIT_FLAG_PROCSPLIT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_BBINSTR:
        return "CORJIT_FLAG_BBINSTR";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_BBINSTR_IF_LOOPS:
        return "CORJIT_FLAG_BBINSTR_IF_LOOPS";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_BBOPT:
        return "CORJIT_FLAG_BBOPT";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_FRAMED:
        return "CORJIT_FLAG_FRAMED";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_PUBLISH_SECRET_PARAM:
        return "CORJIT_FLAG_PUBLISH_SECRET_PARAM";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_USE_PINVOKE_HELPERS:
        return "CORJIT_FLAG_USE_PINVOKE_HELPERS";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_REVERSE_PINVOKE:
        return "CORJIT_FLAG_REVERSE_PINVOKE";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_TRACK_TRANSITIONS:
        return "CORJIT_FLAG_TRACK_TRANSITIONS";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_TIER0:
        return "CORJIT_FLAG_TIER0";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_TIER1:
        return "CORJIT_FLAG_TIER1";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_NO_INLINING:
        return "CORJIT_FLAG_NO_INLINING";

#if defined(TARGET_ARM)
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_RELATIVE_CODE_RELOCS:
        return "CORJIT_FLAG_RELATIVE_CODE_RELOCS";
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_SOFTFP_ABI:
        return "CORJIT_FLAG_SOFTFP_ABI";
#endif // defined(TARGET_ARM)

#if defined(TARGET_X86) || defined(TARGET_AMD64)
    case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_VECTOR512_THROTTLING:
        return "CORJIT_FLAG_VECTOR512_THROTTLING";
#endif // defined(TARGET_XARCH)

    default:
        return "<unknown>";
    }
}

void MethodContext::dmpGetJitFlags(DWORD key, DD value)
{
    CORJIT_FLAGS* jitflags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
    printf("GetJitFlags key %u sizeInBytes-%u jitFlags-%016" PRIX64 " instructionSetFlags-", key, value.B, jitflags->GetFlagsRaw());

    uint64_t *raw = jitflags->GetInstructionSetFlagsRaw();
    const int flagsFieldCount = jitflags->GetInstructionFlagsFieldCount();
    for (int i = flagsFieldCount - 1; i >= 0; i--)
    {
        printf("%016" PRIX64 "", raw[i]);
    }

    // Print text string for the flags
    printf(" (");

    unsigned maxCorJitFlag = sizeof(uint64_t) * 8; // There's no API to determine the largest possible `corJitFlags`.
    for (CORJIT_FLAGS::CorJitFlag flag = (CORJIT_FLAGS::CorJitFlag)0; (unsigned)flag < maxCorJitFlag; flag = (CORJIT_FLAGS::CorJitFlag)((unsigned)flag + 1))
    {
        if (jitflags->IsSet(flag))
        {
            printf(" %s", CorJitFlagToString(flag));
        }
    }

    unsigned maxInstructionSetFlagCount = sizeof(uint64_t) * 8 * flagsFieldCount;
    for (CORINFO_InstructionSet isa = (CORINFO_InstructionSet)0; (unsigned)isa < maxInstructionSetFlagCount; isa = (CORINFO_InstructionSet)((unsigned)isa + 1))
    {
        if (jitflags->IsSet(isa))
        {
            printf(" %s", InstructionSetToString(isa));
        }
    }

    printf(" )");
    GetJitFlags->Unlock();
}
DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes)
{
    DD value = LookupByKeyOrMissNoMessage(GetJitFlags, 0);

    DEBUG_REP(dmpGetJitFlags(0, value));

    CORJIT_FLAGS* resultFlags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A);
    Assert(sizeInBytes >= value.B);
    memcpy((void*)jitFlags, (void*)resultFlags, value.B);
    return value.B;
}

void MethodContext::recGetJitTimeLogFilename(LPCWSTR tempFileName)
{
    if (GetJitTimeLogFilename == nullptr)
        GetJitTimeLogFilename = new LightWeightMap<DWORD, DWORD>();

    DWORD name_index = -1;
    if (tempFileName != nullptr)
    {
        name_index = GetJitTimeLogFilename->AddBuffer((unsigned char*)tempFileName, (DWORD)u16_strlen(tempFileName) + 2);
    }
    GetJitTimeLogFilename->Add(0, name_index);
    DEBUG_REC(dmpGetJitTimeLogFilename(0, name_index));
}
void MethodContext::dmpGetJitTimeLogFilename(DWORD key, DWORD value)
{
    unsigned char* fileName = nullptr;
    if (value != 0)
        fileName = (unsigned char*)GetJitTimeLogFilename->GetBuffer(value);
    printf("GetJitTimeLogFilename key %u, value '%s'", key, fileName);
    GetJitTimeLogFilename->Unlock();
}
LPCWSTR MethodContext::repGetJitTimeLogFilename()
{
    DWORD offset = LookupByKeyOrMissNoMessage(GetJitTimeLogFilename, 0);

    DEBUG_REP(dmpGetJitTimeLogFilename(0, offset));

    LPCWSTR value = nullptr;
    if (offset != 0)
        value = (LPCWSTR)GetJitTimeLogFilename->GetBuffer(offset);
    return value;
}

void MethodContext::recCanInline(CORINFO_METHOD_HANDLE callerHnd,
                                 CORINFO_METHOD_HANDLE calleeHnd,
                                 CorInfoInline         response,
                                 DWORD                 exceptionCode)
{
    if (CanInline == nullptr)
        CanInline = new LightWeightMap<DLDL, Agnostic_CanInline>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(callerHnd);
    key.B = CastHandle(calleeHnd);

    Agnostic_CanInline value;
    value.result           = (DWORD)response;
    value.exceptionCode    = (DWORD)exceptionCode;

    CanInline->Add(key, value);
    DEBUG_REC(dmpCanInline(key, value));
}
void MethodContext::dmpCanInline(DLDL key, const Agnostic_CanInline& value)
{
    printf("CanInline key - callerHnd-%016" PRIX64 " calleeHnd-%016" PRIX64 ", value result-%u exceptionCode-%08X",
           key.A, key.B, value.result, value.exceptionCode);
}
CorInfoInline MethodContext::repCanInline(CORINFO_METHOD_HANDLE callerHnd,
                                          CORINFO_METHOD_HANDLE calleeHnd,
                                          DWORD*                exceptionCode)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(callerHnd);
    key.B = CastHandle(calleeHnd);

    if ((CanInline == nullptr) || (CanInline->GetIndex(key) == -1))
    {
#ifdef sparseMC
        LogDebug("Sparse - repCanInline saying INLINE_FAIL");
        return INLINE_FAIL; // if we have no info, its pretty safe to say we can't inline it.
#else
        LogException(EXCEPTIONCODE_MC, "Didn't find %016" PRIx64 ", %016" PRIx64 ".  probably a missing exception in canInline",
                     key.A, key.B);
#endif
    }

    Agnostic_CanInline value = CanInline->Get(key);
    DEBUG_REP(dmpCanInline(key, value));

    *exceptionCode = value.exceptionCode;

    CorInfoInline response = (CorInfoInline)value.result;
    return response;
}

void MethodContext::recResolveToken(CORINFO_RESOLVED_TOKEN* pResolvedToken, DWORD exceptionCode)
{
    if (ResolveToken == nullptr)
        ResolveToken = new LightWeightMap<Agnostic_CORINFO_RESOLVED_TOKENin, ResolveTokenValue>();

    Agnostic_CORINFO_RESOLVED_TOKENin key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key = SpmiRecordsHelper::CreateAgnostic_CORINFO_RESOLVED_TOKENin(pResolvedToken);

    ResolveTokenValue value;
    if (exceptionCode != ERROR_SUCCESS)
    {
        // The output token memory might be corrupt or uninitialized, so just zero it out.
        // (Set indexes to -1, indicating no buffer).
        ZeroMemory(&value.tokenOut, sizeof(value.tokenOut));
        value.tokenOut.pTypeSpec_Index   = (DWORD)-1;
        value.tokenOut.pMethodSpec_Index = (DWORD)-1;
    }
    else
    {
        value.tokenOut = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKENout(pResolvedToken, ResolveToken);
    }
    value.exceptionCode = exceptionCode;

    ResolveToken->Add(key, value);
    DEBUG_REC(dmpResolveToken(key, value));
}
void MethodContext::dmpResolveToken(const Agnostic_CORINFO_RESOLVED_TOKENin& key, const ResolveTokenValue& value)
{
    printf("ResolveToken key: %s, value: %s excp-%08X",
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKENin(key).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKENout(value.tokenOut).c_str(),
        value.exceptionCode);
}
void MethodContext::repResolveToken(CORINFO_RESOLVED_TOKEN* pResolvedToken, DWORD* exceptionCode)
{
    Agnostic_CORINFO_RESOLVED_TOKENin key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key = SpmiRecordsHelper::CreateAgnostic_CORINFO_RESOLVED_TOKENin(pResolvedToken);

    ResolveTokenValue value = LookupByKeyOrMiss(ResolveToken, key, ": token %x", pResolvedToken->token);
    DEBUG_REP(dmpResolveToken(key, value));

    SpmiRecordsHelper::Restore_CORINFO_RESOLVED_TOKENout(pResolvedToken, value.tokenOut, ResolveToken);
    *exceptionCode = (DWORD)value.exceptionCode;
}

void MethodContext::recGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                   CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken,
                                   CORINFO_METHOD_HANDLE   callerHandle,
                                   CORINFO_CALLINFO_FLAGS  flags,
                                   CORINFO_CALL_INFO*      pResult,
                                   DWORD                   exceptionCode)
{
    if (GetCallInfo == nullptr)
        GetCallInfo = new LightWeightMap<Agnostic_GetCallInfo, Agnostic_CORINFO_CALL_INFO>();

    Agnostic_GetCallInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetCallInfo);

    if (pConstrainedResolvedToken != nullptr)
    {
        key.ConstrainedResolvedToken =
            SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pConstrainedResolvedToken, GetCallInfo);
    }

    key.callerHandle = CastHandle(callerHandle);
    key.flags        = (DWORD)flags;

    Agnostic_CORINFO_CALL_INFO value;
    ZeroMemory(&value, sizeof(Agnostic_CORINFO_CALL_INFO));

    if (exceptionCode == 0)
    {
        value.hMethod     = CastHandle(pResult->hMethod);
        value.methodFlags = (DWORD)pResult->methodFlags;
        value.classFlags  = (DWORD)pResult->classFlags;
        value.sig         = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(pResult->sig, GetCallInfo, SigInstHandleMap);

        value.accessAllowed                   = (DWORD)pResult->accessAllowed;
        value.callsiteCalloutHelper.helperNum = (DWORD)pResult->callsiteCalloutHelper.helperNum;
        value.callsiteCalloutHelper.numArgs   = (DWORD)pResult->callsiteCalloutHelper.numArgs;
        for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
        {
            value.callsiteCalloutHelper.args[i].constant = (DWORDLONG)pResult->callsiteCalloutHelper.args[i].constant;
            value.callsiteCalloutHelper.args[i].argType  = (DWORD)pResult->callsiteCalloutHelper.args[i].argType;
        }
        value.thisTransform = (DWORD)pResult->thisTransform;

        value.kind                           = (DWORD)pResult->kind;
        value.nullInstanceCheck              = (DWORD)pResult->nullInstanceCheck;
        value.contextHandle                  = CastHandle(pResult->contextHandle);
        value.exactContextNeedsRuntimeLookup = (DWORD)pResult->exactContextNeedsRuntimeLookup;

        value.stubLookup = SpmiRecordsHelper::StoreAgnostic_CORINFO_LOOKUP(&pResult->stubLookup);

        value.instParamLookup.accessType = (DWORD)pResult->instParamLookup.accessType;
        value.instParamLookup.handle     = CastHandle(pResult->instParamLookup.handle);
        value.wrapperDelegateInvoke      = (DWORD)pResult->wrapperDelegateInvoke;
    }

    value.exceptionCode = (DWORD)exceptionCode;

    GetCallInfo->Add(key, value);
    DEBUG_REC(dmpGetCallInfo(key, value));
}

void MethodContext::dmpGetCallInfo(const Agnostic_GetCallInfo& key, const Agnostic_CORINFO_CALL_INFO& value)
{
    printf("GetCallInfo key rt{%s} crt{%s} ch-%016" PRIX64 " flg-%08X"
        ", value mth-%016" PRIX64 ", mf-%08X (%s) cf-%08X (%s)"
        " sig-%s"
        " aa-%u"
        " cch{hn-%u na-%u (TODO: dump callsiteCalloutHelper.args)}"
        " tt-%u"
        " k-%u (%s)"
        " nic-%u (%s)"
        " ch-%016" PRIX64 ""
        " ecnrl-%u (%s)"
        " stubLookup{%s}"
        " ipl{at-%08X (%s) hnd-%016" PRIX64 "}"
        " wdi-%u (%s)"
        " excp-%08X",
        // input
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ResolvedToken).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ConstrainedResolvedToken).c_str(),
        key.callerHandle,
        key.flags,
        // output
        value.hMethod,
        value.methodFlags,
        SpmiDumpHelper::DumpCorInfoFlag((CorInfoFlag)value.methodFlags).c_str(),
        value.classFlags,
        SpmiDumpHelper::DumpCorInfoFlag((CorInfoFlag)value.classFlags).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.sig, GetCallInfo, SigInstHandleMap).c_str(),
        value.accessAllowed,
        value.callsiteCalloutHelper.helperNum,
        value.callsiteCalloutHelper.numArgs,
        value.thisTransform,
        value.kind,
        toString((CORINFO_CALL_KIND)value.kind),
        value.nullInstanceCheck,
        (bool)value.nullInstanceCheck ? "true" : "false",
        value.contextHandle,
        value.exactContextNeedsRuntimeLookup,
        (bool)value.exactContextNeedsRuntimeLookup ? "true" : "false",
        SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP(value.stubLookup).c_str(),
        value.instParamLookup.accessType,
        toString((InfoAccessType)value.instParamLookup.accessType),
        value.instParamLookup.handle,
        value.wrapperDelegateInvoke,
        (bool)value.wrapperDelegateInvoke ? "true" : "false",
        value.exceptionCode);
}
void MethodContext::repGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                   CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken,
                                   CORINFO_METHOD_HANDLE   callerHandle,
                                   CORINFO_CALLINFO_FLAGS  flags,
                                   CORINFO_CALL_INFO*      pResult,
                                   DWORD*                  exceptionCode)
{
    AssertMapExistsNoMessage(GetCallInfo);

    Agnostic_GetCallInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetCallInfo);
    if (pConstrainedResolvedToken != nullptr)
    {
        key.ConstrainedResolvedToken =
            SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pConstrainedResolvedToken, GetCallInfo);
    }
    key.callerHandle = CastHandle(callerHandle);
    key.flags        = (DWORD)flags;

    Agnostic_CORINFO_CALL_INFO value = LookupByKeyOrMiss(GetCallInfo, key, ": key %08x, %016" PRIx64 "",
                    key.ResolvedToken.inValue.token, key.ResolvedToken.outValue.hClass);

    if (value.exceptionCode != 0)
    {
        // If there was an exception, the stored data is ignored.
        ZeroMemory(pResult, sizeof(*pResult));
    }
    else
    {
        pResult->hMethod = (CORINFO_METHOD_HANDLE)value.hMethod;
        pResult->methodFlags = (unsigned)value.methodFlags;
        // We could have stored getCallInfo from a previous call in this
        // context without the CORINFO_FLG_DONT_INLINE bit. If the JIT later
        // realized that this will be an always no-inline then it would call
        // setMethodAttribs with that information, but a later getCallInfo call
        // would not update the recorded SPMI entry. In that case let's mimic
        // the behavior on the runtime side by calculating this particular
        // dynamic flag here (the same logic is present in getMethodAttribs).
        //
        // This doesn't handle all cases (e.g. parallelism could still cause
        // us to record these flags without CORINFO_FLG_DONT_INLINE).
        if ((cr->repSetMethodAttribs(pResult->hMethod) & CORINFO_FLG_BAD_INLINEE) != 0)
            pResult->methodFlags |= CORINFO_FLG_DONT_INLINE;

        pResult->classFlags = (unsigned)value.classFlags;
        pResult->sig = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.sig, GetCallInfo, SigInstHandleMap);
        pResult->accessAllowed = (CorInfoIsAccessAllowedResult)value.accessAllowed;
        pResult->callsiteCalloutHelper.helperNum = (CorInfoHelpFunc)value.callsiteCalloutHelper.helperNum;
        pResult->callsiteCalloutHelper.numArgs = (unsigned)value.callsiteCalloutHelper.numArgs;
        for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
        {
            pResult->callsiteCalloutHelper.args[i].constant = (size_t)value.callsiteCalloutHelper.args[i].constant;
            pResult->callsiteCalloutHelper.args[i].argType =
                (CorInfoAccessAllowedHelperArgType)value.callsiteCalloutHelper.args[i].argType;
        }
        pResult->thisTransform = (CORINFO_THIS_TRANSFORM)value.thisTransform;
        pResult->kind = (CORINFO_CALL_KIND)value.kind;
        pResult->nullInstanceCheck = (bool)value.nullInstanceCheck;
        pResult->contextHandle = (CORINFO_CONTEXT_HANDLE)value.contextHandle;
        pResult->exactContextNeedsRuntimeLookup = (bool)value.exactContextNeedsRuntimeLookup;
        pResult->stubLookup = SpmiRecordsHelper::RestoreCORINFO_LOOKUP(value.stubLookup);

        if (pResult->kind == CORINFO_VIRTUALCALL_STUB)
        {
            cr->CallTargetTypes->Add(CastPointer(pResult->stubLookup.constLookup.addr),
                (DWORD)CORINFO_VIRTUALCALL_STUB);
        }
        pResult->instParamLookup.accessType = (InfoAccessType)value.instParamLookup.accessType;
        pResult->instParamLookup.handle = (CORINFO_GENERIC_HANDLE)value.instParamLookup.handle;
        pResult->wrapperDelegateInvoke = (bool)value.wrapperDelegateInvoke;
    }

    *exceptionCode = (DWORD)value.exceptionCode;

    DEBUG_REP(dmpGetCallInfo(key, value));
}

//
// Variant of repGetCallInfo that only requires a method handle, i.e. it performs a reverse lookup to find the
// resolved token info that, along with the given method handle, was passed into getCallInfo.
//
// Arguments:
//    methodHandle - The method handle to find call info for.
//    pResult      - [out] The call info for the given method.
//
// Notes:
//    If this fails to find a recorded call to getCallInfo with the given method handle, this will throw an
//    exception.
//
void MethodContext::repGetCallInfoFromMethodHandle(CORINFO_METHOD_HANDLE methodHandle, CORINFO_CALL_INFO* pResult)
{
    if (GetCallInfo != nullptr)
    {
        for (unsigned int i = 0; i < GetCallInfo->GetCount(); i++)
        {
            Agnostic_GetCallInfo       key = GetCallInfo->GetKey(i);
            Agnostic_CORINFO_CALL_INFO val = GetCallInfo->GetItem(i);

            if ((CORINFO_METHOD_HANDLE)val.hMethod == methodHandle)
            {
                CORINFO_RESOLVED_TOKEN resolvedToken;
                DWORD                  exceptionCode;

                resolvedToken.tokenContext = (CORINFO_CONTEXT_HANDLE)key.ResolvedToken.inValue.tokenContext;
                resolvedToken.tokenScope   = (CORINFO_MODULE_HANDLE)key.ResolvedToken.inValue.tokenScope;
                resolvedToken.token        = (mdToken)key.ResolvedToken.inValue.token;
                resolvedToken.tokenType    = (CorInfoTokenKind)key.ResolvedToken.inValue.tokenType;

                repResolveToken(&resolvedToken, &exceptionCode);

                // If the original call to getCallInfo passed in a null constrainedResolvedToken pointer,
                // then we won't be able to replay it. In that case, we'll need to pass a null pointer into
                // repGetCallInfo for constrainedResolvedToken, instead of just passing the address of our
                // local (but meaningless) constrainedResolvedToken struct.
                CORINFO_RESOLVED_TOKEN  constrainedResolvedToken;
                CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken = nullptr;

                if (key.ConstrainedResolvedToken.inValue.tokenContext != 0 &&
                    key.ConstrainedResolvedToken.inValue.tokenScope != 0)
                {
                    constrainedResolvedToken.tokenContext =
                        (CORINFO_CONTEXT_HANDLE)key.ConstrainedResolvedToken.inValue.tokenContext;
                    constrainedResolvedToken.tokenScope =
                        (CORINFO_MODULE_HANDLE)key.ConstrainedResolvedToken.inValue.tokenScope;
                    constrainedResolvedToken.token = (mdToken)key.ConstrainedResolvedToken.inValue.token;
                    constrainedResolvedToken.tokenType =
                        (CorInfoTokenKind)key.ConstrainedResolvedToken.inValue.tokenType;
                    pConstrainedResolvedToken = &constrainedResolvedToken;

                    repResolveToken(pConstrainedResolvedToken, &exceptionCode);
                }

                repGetCallInfo(&resolvedToken, pConstrainedResolvedToken, (CORINFO_METHOD_HANDLE)key.callerHandle,
                               (CORINFO_CALLINFO_FLAGS)key.flags, pResult, &exceptionCode);
                return;
            }
        }
    }

    // If we reached here, we didn't find a key associated with the given method handle
    LogException(EXCEPTIONCODE_MC, "Didn't find key %016" PRIX64 ".", methodHandle);
}

void MethodContext::recExpandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_GENERICHANDLE_RESULT* pResult)
{
    if (ExpandRawHandleIntrinsic == nullptr)
        ExpandRawHandleIntrinsic = new LightWeightMap<Agnostic_ExpandRawHandleIntrinsic, Agnostic_CORINFO_GENERICHANDLE_RESULT>;

    Agnostic_ExpandRawHandleIntrinsic key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::CreateAgnostic_CORINFO_RESOLVED_TOKENin(pResolvedToken);
    key.hCallerHandle = CastHandle(callerHandle);

    Agnostic_CORINFO_GENERICHANDLE_RESULT value;
    value.lookup            = SpmiRecordsHelper::StoreAgnostic_CORINFO_LOOKUP(&pResult->lookup);
    value.compileTimeHandle = CastHandle(pResult->compileTimeHandle);
    value.handleType        = (DWORD)pResult->handleType;

    ExpandRawHandleIntrinsic->Add(key, value);
    DEBUG_REC(dmpExpandRawHandleIntrinsic(key, value));
}
void MethodContext::dmpExpandRawHandleIntrinsic(const Agnostic_ExpandRawHandleIntrinsic& key, const Agnostic_CORINFO_GENERICHANDLE_RESULT& result)
{
    printf("ExpandRawHandleIntrinsic key: %s, value %s cth-%016" PRIx64 " ht-%u",
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKENin(key.ResolvedToken).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP(result.lookup).c_str(),
        result.compileTimeHandle,
        result.handleType);
}
void MethodContext::repExpandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_GENERICHANDLE_RESULT* pResult)
{
    Agnostic_ExpandRawHandleIntrinsic key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding

    key.ResolvedToken = SpmiRecordsHelper::CreateAgnostic_CORINFO_RESOLVED_TOKENin(pResolvedToken);
    key.hCallerHandle = CastHandle(callerHandle);

    Agnostic_CORINFO_GENERICHANDLE_RESULT value =
        LookupByKeyOrMiss(ExpandRawHandleIntrinsic, key, ": key %x", pResolvedToken->token);

    DEBUG_REP(dmpExpandRawHandleIntrinsic(key, value));

    pResult->lookup            = SpmiRecordsHelper::RestoreCORINFO_LOOKUP(value.lookup);
    pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)value.compileTimeHandle;
    pResult->handleType        = (CorInfoGenericHandleType)value.handleType;
}

void MethodContext::recIsIntrinsicType(CORINFO_CLASS_HANDLE cls, bool result)
{
    if (IsIntrinsicType == nullptr)
        IsIntrinsicType = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = result ? 1 : 0;
    IsIntrinsicType->Add(key, value);
    DEBUG_REC(dmpIsIntrinsicType(key, value));
}
void MethodContext::dmpIsIntrinsicType(DWORDLONG key, DWORD value)
{
    printf("IsIntrinsicType key mth-%016" PRIX64 ", value intr-%u", key, value);
}
bool MethodContext::repIsIntrinsicType(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(IsIntrinsicType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsIntrinsicType(key, value));
    return value != 0;
}

void MethodContext::recAsCorInfoType(CORINFO_CLASS_HANDLE cls, CorInfoType result)
{
    if (AsCorInfoType == nullptr)
        AsCorInfoType = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    AsCorInfoType->Add(key, value);
    DEBUG_REC(dmpAsCorInfoType(key, value));
}
void MethodContext::dmpAsCorInfoType(DWORDLONG key, DWORD value)
{
    printf("AsCorInfoType key cls-%016" PRIX64 ", value cit-%u(%s)", key, value, toString((CorInfoType)value));
}
CorInfoType MethodContext::repAsCorInfoType(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(AsCorInfoType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpAsCorInfoType(key, value));
    CorInfoType result = (CorInfoType)value;
    return result;
}

void MethodContext::recIsValueClass(CORINFO_CLASS_HANDLE cls, bool result)
{
    if (IsValueClass == nullptr)
        IsValueClass = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = result ? 1 : 0;
    IsValueClass->Add(key, value);
    DEBUG_REC(dmpIsValueClass(key, value));
}
void MethodContext::dmpIsValueClass(DWORDLONG key, DWORD value)
{
    printf("IsValueClass key cls-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repIsValueClass(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(IsValueClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsValueClass(key, value));
    return value != 0;
}

void MethodContext::recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result)
{
    if (GetClassSize == nullptr)
        GetClassSize = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetClassSize->Add(key, value);
    DEBUG_REC(dmpGetClassSize(key, value));
}
void MethodContext::dmpGetClassSize(DWORDLONG key, DWORD val)
{
    printf("GetClassSize key %016" PRIX64 ", value %u", key, val);
}
unsigned MethodContext::repGetClassSize(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetClassSize, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetClassSize(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recGetHeapClassSize(CORINFO_CLASS_HANDLE cls, unsigned result)
{
    if (GetHeapClassSize == nullptr)
        GetHeapClassSize = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetHeapClassSize->Add(key, value);
    DEBUG_REC(dmpGetHeapClassSize(key, value));
}
void MethodContext::dmpGetHeapClassSize(DWORDLONG key, DWORD val)
{
    printf("GetHeapClassSize key %016" PRIX64 ", value %u", key, val);
}
unsigned MethodContext::repGetHeapClassSize(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetHeapClassSize, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetHeapClassSize(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recCanAllocateOnStack(CORINFO_CLASS_HANDLE cls, bool result)
{
    if (CanAllocateOnStack == nullptr)
        CanAllocateOnStack = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = result ? 1 : 0;
    CanAllocateOnStack->Add(key, value);
    DEBUG_REC(dmpCanAllocateOnStack(key, value));
}
void MethodContext::dmpCanAllocateOnStack(DWORDLONG key, DWORD val)
{
    printf("CanAllocateOnStack key %016" PRIX64 ", value %u", key, val);
}
bool MethodContext::repCanAllocateOnStack(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(CanAllocateOnStack, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpCanAllocateOnStack(key, value));
    return value != 0;
}

void MethodContext::recGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls, unsigned result)
{
    if (GetClassNumInstanceFields == nullptr)
        GetClassNumInstanceFields = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetClassNumInstanceFields->Add(key, value);
    DEBUG_REC(dmpGetClassNumInstanceFields(key, value));
}
void MethodContext::dmpGetClassNumInstanceFields(DWORDLONG key, DWORD value)
{
    printf("GetClassNumInstanceFields key cls-%016" PRIX64 ", value res-%u", key, value);
}
unsigned MethodContext::repGetClassNumInstanceFields(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetClassNumInstanceFields, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetClassNumInstanceFields(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls, CorInfoHelpFunc result)
{
    if (GetNewArrHelper == nullptr)
        GetNewArrHelper = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(arrayCls);
    DWORD value = (DWORD)result;
    GetNewArrHelper->Add(key, value);
    DEBUG_REC(dmpGetNewArrHelper(key, value));
}
void MethodContext::dmpGetNewArrHelper(DWORDLONG key, DWORD value)
{
    printf("GetNewArrHelper key cls-%016" PRIX64 ", value res-%u", key, value);
}
CorInfoHelpFunc MethodContext::repGetNewArrHelper(CORINFO_CLASS_HANDLE arrayCls)
{
    DWORDLONG key = CastHandle(arrayCls);
    DWORD value = LookupByKeyOrMiss(GetNewArrHelper, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetNewArrHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd, CorInfoHelpFunc result)
{
    if (GetSharedCCtorHelper == nullptr)
        GetSharedCCtorHelper = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(clsHnd);
    DWORD value = (DWORD)result;
    GetSharedCCtorHelper->Add(key, value);
    DEBUG_REC(dmpGetSharedCCtorHelper(key, value));
}
void MethodContext::dmpGetSharedCCtorHelper(DWORDLONG key, DWORD value)
{
    printf("GetSharedCCtorHelper key cls-%016" PRIX64 ", value res-%u", key, value);
}
CorInfoHelpFunc MethodContext::repGetSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd)
{
    DWORDLONG key = CastHandle(clsHnd);
    DWORD value = LookupByKeyOrMiss(GetSharedCCtorHelper, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetSharedCCtorHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recGetTypeForBox(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
    if (GetTypeForBox == nullptr)
        GetTypeForBox = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = CastHandle(result);
    GetTypeForBox->Add(key, value);
    DEBUG_REC(dmpGetTypeForBox(key, value));
}
void MethodContext::dmpGetTypeForBox(DWORDLONG key, DWORDLONG value)
{
    printf("GetTypeForBox key cls-%016" PRIX64 ", value res-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetTypeForBox(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetTypeForBox, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetTypeForBox(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
{
    if (GetBoxHelper == nullptr)
        GetBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetBoxHelper->Add(key, value);
    DEBUG_REC(dmpGetBoxHelper(key, value));
}
void MethodContext::dmpGetBoxHelper(DWORDLONG key, DWORD value)
{
    printf("GetBoxHelper key cls-%016" PRIX64 ", value res-%u", key, value);
}
CorInfoHelpFunc MethodContext::repGetBoxHelper(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetBoxHelper, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetBoxHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recGetBuiltinClass(CorInfoClassId classId, CORINFO_CLASS_HANDLE result)
{
    if (GetBuiltinClass == nullptr)
        GetBuiltinClass = new LightWeightMap<DWORD, DWORDLONG>();

    DWORD key = (DWORD)classId;
    DWORDLONG value = CastHandle(result);
    GetBuiltinClass->Add(key, value);
    DEBUG_REC(dmpGetBuiltinClass(key, value));
}
void MethodContext::dmpGetBuiltinClass(DWORD key, DWORDLONG value)
{
    printf("GetBuiltinClass key cls-%08X, value cls-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetBuiltinClass(CorInfoClassId classId)
{
    DWORD key = (DWORD)classId;
    DWORDLONG value = LookupByKeyOrMiss(GetBuiltinClass, key, ": key %08X", key);
    DEBUG_REP(dmpGetBuiltinClass(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls, CorInfoType result)
{
    if (GetTypeForPrimitiveValueClass == nullptr)
        GetTypeForPrimitiveValueClass = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetTypeForPrimitiveValueClass->Add(key, value);
    DEBUG_REC(dmpGetTypeForPrimitiveValueClass(key, value));
}
void MethodContext::dmpGetTypeForPrimitiveValueClass(DWORDLONG key, DWORD value)
{
    printf("GetTypeForPrimitiveValueClass key cls-%016" PRIX64 ", value cit-%u(%s)", key, value, toString((CorInfoType)value));
}
CorInfoType MethodContext::repGetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetTypeForPrimitiveValueClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetTypeForPrimitiveValueClass(key, value));
    CorInfoType result = (CorInfoType)value;
    return result;
}

void MethodContext::recGetTypeForPrimitiveNumericClass(CORINFO_CLASS_HANDLE cls, CorInfoType result)
{
    if (GetTypeForPrimitiveNumericClass == nullptr)
        GetTypeForPrimitiveNumericClass = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetTypeForPrimitiveNumericClass->Add(key, value);
    DEBUG_REC(dmpGetTypeForPrimitiveNumericClass(key, value));
}
void MethodContext::dmpGetTypeForPrimitiveNumericClass(DWORDLONG key, DWORD value)
{
    printf("GetTypeForPrimitiveNumericClass key cls-%016" PRIX64 ", value cit-%u(%s)", key, value,
           toString((CorInfoType)value));
}
CorInfoType MethodContext::repGetTypeForPrimitiveNumericClass(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetTypeForPrimitiveNumericClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetTypeForPrimitiveNumericClass(key, value));
    CorInfoType result = (CorInfoType)value;
    return result;
}

void MethodContext::recGetParentType(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
    if (GetParentType == nullptr)
        GetParentType = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = CastHandle(result);
    GetParentType->Add(key, value);
    DEBUG_REC(dmpGetParentType(key, value));
}
void MethodContext::dmpGetParentType(DWORDLONG key, DWORDLONG value)
{
    printf("GetParentType key cls-%016" PRIX64 ", value cls-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetParentType(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetParentType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetParentType(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recIsSDArray(CORINFO_CLASS_HANDLE cls, bool result)
{
    if (IsSDArray == nullptr)
        IsSDArray = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = result ? 1 : 0;
    IsSDArray->Add(key, value);
    DEBUG_REC(dmpIsSDArray(key, value));
}
void MethodContext::dmpIsSDArray(DWORDLONG key, DWORD value)
{
    printf("IsSDArray key cls-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repIsSDArray(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(IsSDArray, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsSDArray(key, value));
    return value != 0;
}

void MethodContext::recGetFieldClass(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE result)
{
    if (GetFieldClass == nullptr)
        GetFieldClass = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(field);
    DWORDLONG value = CastHandle(result);
    GetFieldClass->Add(key, value);
    DEBUG_REC(dmpGetFieldClass(key, value));
}
void MethodContext::dmpGetFieldClass(DWORDLONG key, DWORDLONG value)
{
    printf("GetFieldClass key %016" PRIX64 ", value %016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetFieldClass(CORINFO_FIELD_HANDLE field)
{
    DWORDLONG key = CastHandle(field);
    DWORDLONG value = LookupByKeyOrMiss(GetFieldClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetFieldClass(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetFieldOffset(CORINFO_FIELD_HANDLE field, unsigned result)
{
    if (GetFieldOffset == nullptr)
        GetFieldOffset = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(field);
    DWORD value = (DWORD)result;
    GetFieldOffset->Add(key, value);
    DEBUG_REC(dmpGetFieldOffset(key, value));
}
void MethodContext::dmpGetFieldOffset(DWORDLONG key, DWORD value)
{
    printf("GetFieldOffset key FLD-%016" PRIX64 ", value %08X", key, value);
}
unsigned MethodContext::repGetFieldOffset(CORINFO_FIELD_HANDLE field)
{
    DWORDLONG key = CastHandle(field);
    DWORD value = LookupByKeyOrMiss(GetFieldOffset, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetFieldOffset(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle, CorInfoHelpFunc result)
{
    if (GetLazyStringLiteralHelper == nullptr)
        GetLazyStringLiteralHelper = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(handle);
    DWORD value = (DWORD)result;
    GetLazyStringLiteralHelper->Add(key, value);
    DEBUG_REC(dmpGetLazyStringLiteralHelper(key, value));
}
void MethodContext::dmpGetLazyStringLiteralHelper(DWORDLONG key, DWORD value)
{
    printf("GetLazyStringLiteralHelper key mod-%016" PRIX64 ", value res-%u", key, value);
}
CorInfoHelpFunc MethodContext::repGetLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle)
{
    DWORDLONG key = CastHandle(handle);
    DWORD value = LookupByKeyOrMiss(GetLazyStringLiteralHelper, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetLazyStringLiteralHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recGetUnBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result)
{
    if (GetUnBoxHelper == nullptr)
        GetUnBoxHelper = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetUnBoxHelper->Add(key, value);
    DEBUG_REC(dmpGetUnBoxHelper(key, value));
}
void MethodContext::dmpGetUnBoxHelper(DWORDLONG key, DWORD value)
{
    printf("GetUnBoxHelper key cls-%016" PRIX64 ", value res-%u", key, value);
}
CorInfoHelpFunc MethodContext::repGetUnBoxHelper(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetUnBoxHelper, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetUnBoxHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recGetRuntimeTypePointer(CORINFO_CLASS_HANDLE cls, CORINFO_OBJECT_HANDLE result)
{
    if (GetRuntimeTypePointer == nullptr)
        GetRuntimeTypePointer = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = (DWORDLONG)result;
    GetRuntimeTypePointer->Add(key, value);
    DEBUG_REC(dmpGetRuntimeTypePointer(key, value));
}
void MethodContext::dmpGetRuntimeTypePointer(DWORDLONG key, DWORDLONG value)
{
    printf("GetRuntimeTypePointer key cls-%016" PRIX64 ", value res-%016" PRIX64 "", key, value);
}
CORINFO_OBJECT_HANDLE MethodContext::repGetRuntimeTypePointer(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetRuntimeTypePointer, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetRuntimeTypePointer(key, value));
    return (CORINFO_OBJECT_HANDLE)value;
}

void MethodContext::recIsObjectImmutable(CORINFO_OBJECT_HANDLE objPtr, bool result)
{
    if (IsObjectImmutable == nullptr)
        IsObjectImmutable = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = (DWORDLONG)objPtr;
    DWORD value = (DWORD)result;
    IsObjectImmutable->Add(key, value);
    DEBUG_REC(dmpIsObjectImmutable(key, value));
}
void MethodContext::dmpIsObjectImmutable(DWORDLONG key, DWORD value)
{
    printf("IsObjectImmutable key obj-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repIsObjectImmutable(CORINFO_OBJECT_HANDLE objPtr)
{
    DWORDLONG key = (DWORDLONG)objPtr;
    DWORD value = LookupByKeyOrMiss(IsObjectImmutable, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsObjectImmutable(key, value));
    return (bool)value;
}

void MethodContext::recGetStringChar(CORINFO_OBJECT_HANDLE strObj, int index, uint16_t* charValue, bool result)
{
    if (GetStringChar == nullptr)
        GetStringChar = new LightWeightMap<DLD, DD>();

    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(strObj);
    key.B = (DWORD)index;

    DD value;
    ZeroMemory(&value, sizeof(value));
    value.A = (DWORD)result;
    value.B = (DWORD)*charValue;

    GetStringChar->Add(key, value);
    DEBUG_REC(dmpGetStringChar(key, value));
}
void MethodContext::dmpGetStringChar(DLD key, DD value)
{
    printf("IsObjectImmutable key obj-%016" PRIX64 ", index-%u, value res-%u, val-%u", key.A, key.B, value.A, value.B);
}
bool MethodContext::repGetStringChar(CORINFO_OBJECT_HANDLE strObj, int index, uint16_t* charValue)
{
    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(strObj);
    key.B = (DWORD)index;

    DD value = LookupByKeyOrMiss(GetStringChar, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetStringChar(key, value));
    *charValue = (uint16_t)value.B;
    return (bool)value.A;
}

void MethodContext::recGetObjectType(CORINFO_OBJECT_HANDLE objPtr, CORINFO_CLASS_HANDLE result)
{
    if (GetObjectType == nullptr)
        GetObjectType = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = (DWORDLONG)objPtr;
    DWORDLONG value = (DWORDLONG)result;
    GetObjectType->Add(key, value);
    DEBUG_REC(dmpGetObjectType(key, value));
}
void MethodContext::dmpGetObjectType(DWORDLONG key, DWORDLONG value)
{
    printf("GetObjectType key obj-%016" PRIX64 ", value res-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetObjectType(CORINFO_OBJECT_HANDLE objPtr)
{
    DWORDLONG key = (DWORDLONG)objPtr;
    DWORDLONG value = LookupByKeyOrMiss(GetObjectType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetObjectType(key, value));
    return (CORINFO_CLASS_HANDLE)value;
}

void MethodContext::recGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                           CORINFO_LOOKUP_KIND*    pGenericLookupKind,
                                           CorInfoHelpFunc         id,
                                           CORINFO_METHOD_HANDLE   callerHandle,
                                           CORINFO_CONST_LOOKUP*   pLookup,
                                           bool                    result)
{
    if (GetReadyToRunHelper == nullptr)
        GetReadyToRunHelper = new LightWeightMap<GetReadyToRunHelper_TOKENin, GetReadyToRunHelper_TOKENout>();

    GetReadyToRunHelper_TOKENin key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetReadyToRunHelper);
    key.GenericLookupKind = SpmiRecordsHelper::CreateAgnostic_CORINFO_LOOKUP_KIND(pGenericLookupKind);
    key.id                = (DWORD)id;
    key.callerHandle      = CastHandle(callerHandle);
    GetReadyToRunHelper_TOKENout value;
    value.Lookup = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(pLookup);
    value.result = result;

    GetReadyToRunHelper->Add(key, value);
    DEBUG_REC(dmpGetReadyToRunHelper(key, value));
}

void MethodContext::dmpGetReadyToRunHelper(GetReadyToRunHelper_TOKENin key, GetReadyToRunHelper_TOKENout value)
{
    printf("GetReadyToRunHelper key: tk{%s} kind{%s} id-%u",
           SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ResolvedToken).c_str(),
           SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP_KIND(key.GenericLookupKind).c_str(), key.id);
    printf(", value: lk{ %s } %u", SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.Lookup).c_str(),
           value.result);
}

bool MethodContext::repGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                           CORINFO_LOOKUP_KIND*    pGenericLookupKind,
                                           CorInfoHelpFunc         id,
                                           CORINFO_METHOD_HANDLE   callerHandle,
                                           CORINFO_CONST_LOOKUP*   pLookup)
{
    AssertMapExistsNoMessage(GetReadyToRunHelper);

    GetReadyToRunHelper_TOKENin key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken     = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetReadyToRunHelper);
    key.GenericLookupKind = SpmiRecordsHelper::CreateAgnostic_CORINFO_LOOKUP_KIND(pGenericLookupKind);
    key.id                = (DWORD)id;
    key.callerHandle      = CastHandle(callerHandle);

    GetReadyToRunHelper_TOKENout value = LookupByKeyOrMissNoMessage(GetReadyToRunHelper, key);

    DEBUG_REP(dmpGetReadyToRunHelper(key, value));

    *pLookup = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.Lookup);
    return value.result;
}

void MethodContext::recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
                                                       mdToken                 targetConstraint,
                                                       CORINFO_CLASS_HANDLE    delegateType,
                                                       CORINFO_METHOD_HANDLE   callerHandle,
                                                       CORINFO_LOOKUP*         pLookup)
{
    if (GetReadyToRunDelegateCtorHelper == nullptr)
        GetReadyToRunDelegateCtorHelper =
            new LightWeightMap<GetReadyToRunDelegateCtorHelper_TOKENIn, Agnostic_CORINFO_LOOKUP>();

    GetReadyToRunDelegateCtorHelper_TOKENIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.TargetMethod =
        SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pTargetMethod, GetReadyToRunDelegateCtorHelper);
    key.targetConstraint          = targetConstraint;
    key.delegateType              = CastHandle(delegateType);
    key.callerHandle              = CastHandle(callerHandle);
    Agnostic_CORINFO_LOOKUP value = SpmiRecordsHelper::StoreAgnostic_CORINFO_LOOKUP(pLookup);
    GetReadyToRunDelegateCtorHelper->Add(key, value);
    DEBUG_REC(dmpGetReadyToRunDelegateCtorHelper(key, value));
}

void MethodContext::dmpGetReadyToRunDelegateCtorHelper(GetReadyToRunDelegateCtorHelper_TOKENIn key,
                                                       Agnostic_CORINFO_LOOKUP                 value)
{
    printf("GetReadyToRunDelegateCtorHelper key: method tk{%s} type-%016" PRIX64 " constraint-%08X",
           SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.TargetMethod).c_str(), key.delegateType, key.targetConstraint);
    printf(", value: %s", SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP(value).c_str());
}

void MethodContext::repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
                                                       mdToken                 targetConstraint,
                                                       CORINFO_CLASS_HANDLE    delegateType,
                                                       CORINFO_METHOD_HANDLE   callerHandle,
                                                       CORINFO_LOOKUP*         pLookup)
{
    AssertMapExistsNoMessage(GetReadyToRunDelegateCtorHelper);

    GetReadyToRunDelegateCtorHelper_TOKENIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.TargetMethod =
        SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pTargetMethod, GetReadyToRunDelegateCtorHelper);
    key.targetConstraint = targetConstraint;
    key.delegateType     = CastHandle(delegateType);
    key.callerHandle     = CastHandle(callerHandle);

    Agnostic_CORINFO_LOOKUP value = LookupByKeyOrMissNoMessage(GetReadyToRunDelegateCtorHelper, key);

    DEBUG_REP(dmpGetReadyToRunDelegateCtorHelper(key, value));

    *pLookup = SpmiRecordsHelper::RestoreCORINFO_LOOKUP(value);
}

void MethodContext::recGetHelperFtn(CorInfoHelpFunc ftnNum, void** ppIndirection, void* result)
{
    if (GetHelperFtn == nullptr)
        GetHelperFtn = new LightWeightMap<DWORD, DLDL>();

    DWORD key = (DWORD)ftnNum;

    DLDL value;
    value.A = CastPointer(*ppIndirection);
    value.B = CastPointer(result);

    GetHelperFtn->Add(key, value);
    DEBUG_REC(dmpGetHelperFtn(key, value));
}
void MethodContext::dmpGetHelperFtn(DWORD key, DLDL value)
{
    printf("GetHelperFtn key ftn-%u, value ppi-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
void* MethodContext::repGetHelperFtn(CorInfoHelpFunc ftnNum, void** ppIndirection)
{
    DWORD key = (DWORD)ftnNum;
    DLDL value = LookupByKeyOrMiss(GetHelperFtn, key, ": key %u", key);
    DEBUG_REP(dmpGetHelperFtn(key, value));

    *ppIndirection = (void*)value.A;
    return (void*)value.B;
}

//
// Finds the identifier (i.e. the CorInfoHelpFunc enum) of a helper function, based on the address where it
// is located in memory.
//
// Arguments:
//    functionAddress - The starting address of the helper function in memory.
//    pResult         - [out] Pointer to write out the identifier of the helper function located at the given
//                      address.
//
// Return Value:
//    True if there is a helper function associated with the given target address; false otherwise.
//
// Notes:
//    - This might not work correctly with method contexts recorded via NGen compilation; it doesn't compare
//      the ppIndirection value.
//
bool MethodContext::fndGetHelperFtn(void* functionAddress, CorInfoHelpFunc* pResult)
{
    if (GetHelperFtn != nullptr)
    {
        for (unsigned int i = 0; i < GetHelperFtn->GetCount(); i++)
        {
            DWORD key = GetHelperFtn->GetKey(i);
            DLDL  val = GetHelperFtn->GetItem(i);

            // TODO-Cleanup: this only compares the function addresses, and doesn't account for
            // ppIndirection, which will break if the helper is a dynamic helper function.
            if (val.B == CastPointer(functionAddress))
            {
                *pResult = (CorInfoHelpFunc)key;
                return true;
            }
        }
    }

    LogDebug("fndGetHelperFtn - didn't find value %p", functionAddress);
    return false;
}

void MethodContext::recGetJustMyCodeHandle(CORINFO_METHOD_HANDLE         method,
                                           CORINFO_JUST_MY_CODE_HANDLE** ppIndirection,
                                           CORINFO_JUST_MY_CODE_HANDLE   result)
{
    if (GetJustMyCodeHandle == nullptr)
        GetJustMyCodeHandle = new LightWeightMap<DWORDLONG, DLDL>();

    DLDL value;
    value.A = CastPointer(*ppIndirection);
    value.B = CastHandle(result);

    DWORDLONG key = CastHandle(method);
    GetJustMyCodeHandle->Add(key, value);
    DEBUG_REC(dmpGetJustMyCodeHandle(key, value));
}
void MethodContext::dmpGetJustMyCodeHandle(DWORDLONG key, DLDL value)
{
    printf("GetJustMyCodeHandle key ftn-%016" PRIX64 ", value pp-%016" PRIX64 ", res-%016" PRIX64 "", key, value.A, value.B);
}
CORINFO_JUST_MY_CODE_HANDLE MethodContext::repGetJustMyCodeHandle(CORINFO_METHOD_HANDLE         method,
                                                                  CORINFO_JUST_MY_CODE_HANDLE** ppIndirection)
{
    DWORDLONG key = CastHandle(method);
    DLDL value = LookupByKeyOrMiss(GetJustMyCodeHandle, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetJustMyCodeHandle(key, value));

    *ppIndirection                     = (CORINFO_JUST_MY_CODE_HANDLE*)value.A;
    CORINFO_JUST_MY_CODE_HANDLE result = (CORINFO_JUST_MY_CODE_HANDLE)value.B;
    return result;
}

void MethodContext::recGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn,
                                             CORINFO_CONST_LOOKUP* pResult,
                                             CORINFO_ACCESS_FLAGS  accessFlags)
{
    if (GetFunctionEntryPoint == nullptr)
        GetFunctionEntryPoint = new LightWeightMap<DLD, DLD>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A   = CastHandle(ftn);
    key.B   = (DWORD)accessFlags;

    DLD value;
    value.A = CastPointer(pResult->addr); // First union member
    value.B = (DWORD)pResult->accessType;

    GetFunctionEntryPoint->Add(key, value);
    DEBUG_REC(dmpGetFunctionEntryPoint(key, value));
}
void MethodContext::dmpGetFunctionEntryPoint(DLD key, DLD value)
{
    printf("GetFunctionEntryPoint key ftn-%016" PRIX64 " af-%08X, value add-%016" PRIX64 " at-%u", key.A, key.B, value.A, value.B);
}
void MethodContext::repGetFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn,
                                             CORINFO_CONST_LOOKUP* pResult,
                                             CORINFO_ACCESS_FLAGS  accessFlags)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(ftn);
    key.B = (DWORD)accessFlags;

    if (GetFunctionEntryPoint == nullptr)
    {
#ifdef sparseMC
        LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
        pResult->accessType = (InfoAccessType)IAT_PVALUE;
        pResult->addr       = (void*)(CastHandle(ftn) + 0x1c);
        return;
#else
        LogException(EXCEPTIONCODE_MC, "Didn't find %016" PRIX64 ", %8x", CastHandle(ftn), accessFlags);
#endif
    }
    if (GetFunctionEntryPoint->GetIndex(key) == -1)
    {
#ifdef sparseMC
        key.B ^= (DWORD)CORINFO_ACCESS_NONNULL;
        if (GetFunctionEntryPoint->GetIndex(key) != -1)
        {
            LogDebug("Sparse - repGetFunctionEntryPoint found result with inverted CORINFO_ACCESS_NONNULL");
        }
        else
        {
            LogDebug("Sparse - repGetFunctionEntryPoint fabricated result for request.");
            pResult->accessType = (InfoAccessType)IAT_PVALUE;
            pResult->addr       = (void*)(CastHandle(ftn) + 0x1c);
            return;
        }
#else
        LogException(EXCEPTIONCODE_MC, "Didn't find %016" PRIX64 ", %8x", CastHandle(ftn), accessFlags);
#endif
    }

    DLD value = GetFunctionEntryPoint->Get(key);
    DEBUG_REP(dmpGetFunctionEntryPoint(key, value));

    pResult->accessType = (InfoAccessType)value.B;
    pResult->addr       = (void*)value.A;
}

//
// Finds the method handle associated with a method, based on the address where its generated code is located
// in memory.
//
// Arguments:
//    methodAddress - The starting address of the generated code for the method.
//    pResult       - [out] Pointer to a method handle to write into. If this successfully finds a method
//                    handle associated with the given target address, it will be written to here.
//
// Return Value:
//    True if there is a function associated with the given target address; false otherwise.
//
// Assumptions:
//    - The given method address does not point to a jump stub.
//    - The given method is not a generic method.
//    - Only the lower 32 bits of the method address are necessary to identify the method.
//
// Notes:
//    On 64-bit platforms, this only checks if the lower 32 bits of the method address match a recorded
//    function entry point because, on AMD64, this only supports reverse lookups for near calls, which
//    encode their target address as a 32-bit PC-relative displacement.
//
//    Practically speaking, there are two concrete reasons why we ignore the upper 64 bits. First, on
//    AMD64, when the JIT emits a near call, it records the displacement it encodes into the call with
//    the EE as a 32-bit relative "relocation". Consequently, this leads to the second reason why we
//    ignore the upper 64 bits: when SuperPMI is replaying method compilation, it patches up addresses
//    based on the "relocations" the JIT had previously recorded with the EE. Since these relocations
//    are only 32-bit deltas, what you'll usually end up seeing is that, after SuperPMI has applied
//    these fixups, the lower 32 bits of method addresses will match, but the upper 32 bits will differ.
//
bool MethodContext::fndGetFunctionEntryPoint(DLD value, CORINFO_METHOD_HANDLE* pResult)
{
    if (GetFunctionEntryPoint != nullptr)
    {
        for (unsigned int i = 0; i < GetFunctionEntryPoint->GetCount(); i++)
        {
            DLD key = GetFunctionEntryPoint->GetKey(i);
            DLD val = GetFunctionEntryPoint->GetItem(i);

            // TODO-Cleanup: we should be more conscious of the rest of the information in CORINFO_CONST_LOOKUP
            if ((DWORD)val.A == (DWORD)value.A)
            {
                *pResult = (CORINFO_METHOD_HANDLE)key.A;
                return true;
            }
        }
    }

    LogDebug("fndGetFunctionEntryPoint - didn't find value %016" PRIX64 "", value.A);
    return false;
}

void MethodContext::recConstructStringLiteral(CORINFO_MODULE_HANDLE moduleHandle,
                                              mdToken               metaTok,
                                              void*                 pValue,
                                              InfoAccessType        result)
{
    if (ConstructStringLiteral == nullptr)
        ConstructStringLiteral = new LightWeightMap<DLD, DLD>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(moduleHandle);
    key.B = (DWORD)metaTok;

    DLD value;
    value.A = CastPointer(pValue);
    value.B = (DWORD)result;

    ConstructStringLiteral->Add(key, value);
    DEBUG_REC(dmpConstructStringLiteral(key, value));
}
void MethodContext::dmpConstructStringLiteral(DLD key, DLD value)
{
    printf("ConstructStringLiteral key mod-%016" PRIX64 " tok-%08X, value pp-%016" PRIX64 " iat-%u", key.A, key.B, value.A, value.B);
}
InfoAccessType MethodContext::repConstructStringLiteral(CORINFO_MODULE_HANDLE moduleHandle, mdToken metaTok, void** ppValue)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(moduleHandle);
    key.B = (DWORD)metaTok;

    DLD value = LookupByKeyOrMiss(ConstructStringLiteral, key, ": key %016" PRIX64 "", CastHandle(moduleHandle));

    DEBUG_REP(dmpConstructStringLiteral(key, value));

    *ppValue = (void*)value.A;
    return (InfoAccessType)value.B;
}

void MethodContext::recConvertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fMustConvert, bool result)
{
    if (ConvertPInvokeCalliToCall == nullptr)
        ConvertPInvokeCalliToCall = new LightWeightMap<DLD, DWORDLONG>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(pResolvedToken->tokenScope);
    key.B = (DWORD)pResolvedToken->token;

    DWORDLONG value = CastHandle(result ? pResolvedToken->hMethod : 0);

    ConvertPInvokeCalliToCall->Add(key, value);
    DEBUG_REC(dmpConvertPInvokeCalliToCall(key, value));
}
void MethodContext::dmpConvertPInvokeCalliToCall(DLD key, DWORDLONG value)
{
    printf("ConvertPInvokeCalliToCall key mod-%016" PRIX64 " tok-%08X, value %016" PRIX64 "", key.A, key.B, value);
}
bool MethodContext::repConvertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fMustConvert)
{
    AssertMapExistsNoMessage(ConvertPInvokeCalliToCall);

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(pResolvedToken->tokenScope);
    key.B = (DWORD)pResolvedToken->token;

    DWORDLONG value = LookupByKeyOrMissNoMessage(ConvertPInvokeCalliToCall, key);
    DEBUG_REP(dmpConvertPInvokeCalliToCall(key, value));

    pResolvedToken->hMethod = (CORINFO_METHOD_HANDLE)value;
    return value != 0;
}

void MethodContext::recEmptyStringLiteral(void** pValue, InfoAccessType result)
{
    if (EmptyStringLiteral == nullptr)
        EmptyStringLiteral = new DenseLightWeightMap<DLD>();
    DLD temp2;
    temp2.A = CastPointer(*pValue);
    temp2.B = (DWORD)result;

    EmptyStringLiteral->Append(temp2);
}
void MethodContext::dmpEmptyStringLiteral(DWORD key, DLD value)
{
    printf("EmptyStringLiteral key %u, value pVal-%016" PRIX64 " res-%u", key, value.A, value.B);
}
InfoAccessType MethodContext::repEmptyStringLiteral(void** ppValue)
{
    // TODO-Cleanup: sketchy if someone calls this twice
    DLD temp2;
    temp2 = LookupByKeyOrMissNoMessage(EmptyStringLiteral, 0);

    *ppValue = (void*)temp2.A;
    return (InfoAccessType)temp2.B;
}

void MethodContext::recGetArgType(CORINFO_SIG_INFO*       sig,
                                  CORINFO_ARG_LIST_HANDLE args,
                                  CORINFO_CLASS_HANDLE*   vcTypeRet,
                                  CorInfoTypeWithMod      result,
                                  DWORD                   exceptionCode)
{
    if (GetArgType == nullptr)
        GetArgType = new LightWeightMap<Agnostic_GetArgType_Key, Agnostic_GetArgType_Value>();

    Agnostic_GetArgType_Key key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding

    // Only setting values for CORINFO_SIG_INFO things the EE seems to pay attention to... this is necessary since some of the values
    // are unset and fail our precise comparisons ...
    // TODO: verify that the above comment is still true (that some of the fields of incoming argument `sig` contain garbage), or
    // can we store the whole thing and use StoreAgnostic_CORINFO_SIG_INFO()?

    key.flags           = (DWORD)sig->flags;
    key.numArgs         = (DWORD)sig->numArgs;

    SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INST_HandleArray(sig->sigInst.classInstCount, sig->sigInst.classInst, SigInstHandleMap, &key.sigInst_classInstCount, &key.sigInst_classInst_Index);
    SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INST_HandleArray(sig->sigInst.methInstCount, sig->sigInst.methInst, SigInstHandleMap, &key.sigInst_methInstCount, &key.sigInst_methInst_Index);

    key.methodSignature = CastPointer(sig->methodSignature);
    key.scope           = CastHandle(sig->scope);
    key.args            = CastHandle(args);

    Agnostic_GetArgType_Value value;
    value.vcTypeRet     = CastHandle(*vcTypeRet);
    value.result        = (DWORD)result;
    value.exceptionCode = (DWORD)exceptionCode;

    GetArgType->Add(key, value);
    DEBUG_REC(dmpGetArgType(key, value));
}

void MethodContext::dmpGetArgType(const Agnostic_GetArgType_Key& key, const Agnostic_GetArgType_Value& value)
{
    printf("GetArgType key flg-%08X na-%u %s %s msig-%016" PRIX64 " scp-%016" PRIX64 " arg-%016" PRIX64 "",
        key.flags, key.numArgs,
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INST_Element("", "cc", "ci", key.sigInst_classInstCount, key.sigInst_classInst_Index, SigInstHandleMap).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INST_Element("", "mc", "mi", key.sigInst_methInstCount, key.sigInst_methInst_Index, SigInstHandleMap).c_str(),
        key.methodSignature, key.scope, key.args);
    printf(", value result(cit)-%u(%s) vcType-%016" PRIX64 " excp-%08X", value.result, toString((CorInfoTypeWithMod)value.result), value.vcTypeRet, value.exceptionCode);
}

CorInfoTypeWithMod MethodContext::repGetArgType(CORINFO_SIG_INFO*       sig,
                                                CORINFO_ARG_LIST_HANDLE args,
                                                CORINFO_CLASS_HANDLE*   vcTypeRet,
                                                DWORD*                  exceptionCode)
{
    AssertMapExists(GetArgType, ": key %016" PRIX64 " %016" PRIX64 "", CastHandle(sig->scope), CastHandle(args));

    Agnostic_GetArgType_Key key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.flags                  = (DWORD)sig->flags;
    key.numArgs                = (DWORD)sig->numArgs;
    key.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
    key.sigInst_methInstCount  = (DWORD)sig->sigInst.methInstCount;
    key.methodSignature        = CastPointer(sig->methodSignature);
    key.scope                  = CastHandle(sig->scope);
    key.args                   = CastHandle(args);

    key.sigInst_classInst_Index = SpmiRecordsHelper::ContainsHandleMap(sig->sigInst.classInstCount, sig->sigInst.classInst, SigInstHandleMap);
    key.sigInst_methInst_Index  = SpmiRecordsHelper::ContainsHandleMap(sig->sigInst.methInstCount, sig->sigInst.methInst, SigInstHandleMap);

    Agnostic_GetArgType_Value value = LookupByKeyOrMiss(GetArgType, key, ": key %016" PRIX64 " %016" PRIX64 "", key.scope, key.args);

    DEBUG_REP(dmpGetArgType(key, value));

    *vcTypeRet              = (CORINFO_CLASS_HANDLE)value.vcTypeRet;
    CorInfoTypeWithMod temp = (CorInfoTypeWithMod)value.result;
    *exceptionCode          = (DWORD)value.exceptionCode;
    return temp;
}

void MethodContext::recGetExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet, int result)
{
    if (GetExactClasses == nullptr)
        GetExactClasses = new LightWeightMap<DLD, Agnostic_GetExactClassesResult>();

    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(baseType);
    key.B = maxExactClasses;

    int numResults = result < 0 ? 0 : result;

    DWORDLONG* exactClassesAgnostic = new DWORDLONG[numResults];
    for (int i = 0; i < numResults; i++)
        exactClassesAgnostic[i] = CastHandle(exactClsRet[i]);

    Agnostic_GetExactClassesResult value;
    value.numClasses = result;
    value.classes = GetExactClasses->AddBuffer((unsigned char*)exactClassesAgnostic, (unsigned int)(numResults * sizeof(DWORDLONG)));

    delete[] exactClassesAgnostic;

    GetExactClasses->Add(key, value);
}
void MethodContext::dmpGetExactClasses(DLD key, const Agnostic_GetExactClassesResult& value)
{
    printf("GetExactClasses key baseType-%016" PRIX64 ", maxExactCls-%u, value numClasses-%d",
        key.A, key.B, value.numClasses);
}
int MethodContext::repGetExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet)
{
    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(baseType);
    key.B = maxExactClasses;

    Agnostic_GetExactClassesResult value = LookupByKeyOrMiss(GetExactClasses, key, ": key %016" PRIX64 " %08X", key.A, key.B);

    Assert(maxExactClasses >= value.numClasses);

    unsigned char* buffer = GetExactClasses->GetBuffer(value.classes);
    for (int i = 0; i < value.numClasses; i++)
    {
        DWORDLONG handle;
        memcpy(&handle, &buffer[i * sizeof(DWORDLONG)], sizeof(handle));
        exactClsRet[i] = (CORINFO_CLASS_HANDLE)handle;
    }

    return value.numClasses;
}

void MethodContext::recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result)
{
    if (GetArgNext == nullptr)
        GetArgNext = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(args);
    DWORDLONG value = CastHandle(result);
    GetArgNext->Add(key, value);
    DEBUG_REC(dmpGetArgNext(key, value));
}
void MethodContext::dmpGetArgNext(DWORDLONG key, DWORDLONG value)
{
    printf("GetArgNext key %016" PRIX64 ", value %016" PRIX64 "", key, value);
}
CORINFO_ARG_LIST_HANDLE MethodContext::repGetArgNext(CORINFO_ARG_LIST_HANDLE args)
{
    DWORDLONG key = CastHandle(args);
    DWORDLONG value = LookupByKeyOrMiss(GetArgNext, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetArgNext(key, value));
    CORINFO_ARG_LIST_HANDLE result = (CORINFO_ARG_LIST_HANDLE)value;
    return result;
}
void MethodContext::recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE memberParent)
{
    if (GetMethodSig == nullptr)
        GetMethodSig = new LightWeightMap<DLDL, Agnostic_CORINFO_SIG_INFO>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(ftn);
    key.B = CastHandle(memberParent);

    Agnostic_CORINFO_SIG_INFO value = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(*sig, GetMethodSig, SigInstHandleMap);

    GetMethodSig->Add(key, value);
    DEBUG_REC(dmpGetMethodSig(key, value));
}
void MethodContext::dmpGetMethodSig(DLDL key, const Agnostic_CORINFO_SIG_INFO& value)
{
    printf("GetMethodSig key ftn-%016" PRIX64 " prt-%016" PRIX64 ", value-%s", key.A, key.B,
           SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value, GetMethodSig, SigInstHandleMap).c_str());
}
void MethodContext::repGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE memberParent)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(ftn);
    key.B = CastHandle(memberParent);

    Agnostic_CORINFO_SIG_INFO value = LookupByKeyOrMiss(GetMethodSig, key, ": key ftn-%016" PRIX64 " prt-%016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpGetMethodSig(key, value));

    *sig = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value, GetMethodSig, SigInstHandleMap);
}

void MethodContext::recGetArgClass(CORINFO_SIG_INFO*       sig,
                                   CORINFO_ARG_LIST_HANDLE args,
                                   CORINFO_CLASS_HANDLE    result,
                                   DWORD                   exceptionCode)
{
    if (GetArgClass == nullptr)
        GetArgClass = new LightWeightMap<Agnostic_GetArgClass_Key, Agnostic_GetArgClass_Value>();

    Agnostic_GetArgClass_Key key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding

    // Only setting values for CORINFO_SIG_INFO things the EE seems to pay attention to... this is necessary since some of the values
    // are unset and fail our precise comparisons...

    SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INST_HandleArray(sig->sigInst.classInstCount, sig->sigInst.classInst, SigInstHandleMap, &key.sigInst_classInstCount, &key.sigInst_classInst_Index);
    SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INST_HandleArray(sig->sigInst.methInstCount, sig->sigInst.methInst, SigInstHandleMap, &key.sigInst_methInstCount, &key.sigInst_methInst_Index);

    key.methodSignature = CastPointer(sig->methodSignature);
    key.scope           = CastHandle(sig->scope);
    key.args            = CastHandle(args);

    Agnostic_GetArgClass_Value value;
    value.result        = CastHandle(result);
    value.exceptionCode = exceptionCode;

    GetArgClass->Add(key, value);
    DEBUG_REC(dmpGetArgClass(key, value));
}
void MethodContext::dmpGetArgClass(const Agnostic_GetArgClass_Key& key, const Agnostic_GetArgClass_Value& value)
{
    printf("GetArgClass key %s %s msig-%016" PRIX64 " scp-%016" PRIX64 " args-%016" PRIX64 "",
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INST_Element("", "cc", "ci", key.sigInst_classInstCount, key.sigInst_classInst_Index, SigInstHandleMap).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INST_Element("", "mc", "mi", key.sigInst_methInstCount, key.sigInst_methInst_Index, SigInstHandleMap).c_str(),
        key.methodSignature, key.scope, key.args);
    printf(", value %016" PRIX64 " excp-%08X", value.result, value.exceptionCode);
}
CORINFO_CLASS_HANDLE MethodContext::repGetArgClass(CORINFO_SIG_INFO*       sig,
                                                   CORINFO_ARG_LIST_HANDLE args,
                                                   DWORD*                  exceptionCode)
{
    Agnostic_GetArgClass_Key key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.sigInst_classInstCount = (DWORD)sig->sigInst.classInstCount;
    key.sigInst_methInstCount  = (DWORD)sig->sigInst.methInstCount;
    key.methodSignature        = CastPointer(sig->methodSignature);
    key.scope                  = CastHandle(sig->scope);
    key.args                   = CastHandle(args);
    key.sigInst_classInst_Index = SpmiRecordsHelper::ContainsHandleMap(sig->sigInst.classInstCount, sig->sigInst.classInst, SigInstHandleMap);
    key.sigInst_methInst_Index  = SpmiRecordsHelper::ContainsHandleMap(sig->sigInst.methInstCount, sig->sigInst.methInst, SigInstHandleMap);

    Agnostic_GetArgClass_Value value = LookupByKeyOrMiss(GetArgClass, key, ": key %016" PRIX64 " %016" PRIX64 "", key.scope, key.args);

    DEBUG_REP(dmpGetArgClass(key, value));

    *exceptionCode = value.exceptionCode;
    return (CORINFO_CLASS_HANDLE)value.result;
}

void MethodContext::recGetHFAType(CORINFO_CLASS_HANDLE clsHnd, CorInfoHFAElemType result)
{
    if (GetHFAType == nullptr)
        GetHFAType = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(clsHnd);
    DWORD value = (DWORD)result;
    GetHFAType->Add(key, value);
    DEBUG_REC(dmpGetHFAType(key, value));
}
void MethodContext::dmpGetHFAType(DWORDLONG key, DWORD value)
{
    printf("GetHFAType key %016" PRIX64 ", value %u ", key, value);
}
CorInfoHFAElemType MethodContext::repGetHFAType(CORINFO_CLASS_HANDLE clsHnd)
{
    DWORDLONG key = CastHandle(clsHnd);
    DWORD value = LookupByKeyOrMiss(GetHFAType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetHFAType(key, value));
    return (CorInfoHFAElemType)value;
}

void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE  ftn,
                                     CORINFO_METHOD_INFO*   info,
                                     CORINFO_CONTEXT_HANDLE context,
                                     bool                   result,
                                     DWORD                  exceptionCode)
{
    if (GetMethodInfo == nullptr)
        GetMethodInfo = new LightWeightMap<DLDL, Agnostic_GetMethodInfo>();

    Agnostic_GetMethodInfo value;
    ZeroMemory(&value, sizeof(value));

    if (result)
    {
        value.info.ftn           = CastHandle(info->ftn);
        value.info.scope         = CastHandle(info->scope);
        value.info.ILCode_offset = (DWORD)GetMethodInfo->AddBuffer(info->ILCode, info->ILCodeSize);
        value.info.ILCodeSize    = (DWORD)info->ILCodeSize;
        value.info.maxStack      = (DWORD)info->maxStack;
        value.info.EHcount       = (DWORD)info->EHcount;
        value.info.options       = (DWORD)info->options;
        value.info.regionKind    = (DWORD)info->regionKind;

        value.info.args   = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(info->args, GetMethodInfo, SigInstHandleMap);
        value.info.locals = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(info->locals, GetMethodInfo, SigInstHandleMap);
    }
    value.result        = result;
    value.exceptionCode = (DWORD)exceptionCode;

    DLDL key;
    key.A = CastHandle(ftn);
    key.B = CastHandle(context);
    GetMethodInfo->Add(key, value);
    DEBUG_REC(dmpGetMethodInfo(key, value));
}
void MethodContext::dmpGetMethodInfo(DLDL key, const Agnostic_GetMethodInfo& value)
{
    if (value.result)
    {
        printf("GetMethodInfo key ftn-%016" PRIX64 " ctx-%016" PRIX64 ", value res-%u ftn-%016" PRIX64 " scp-%016" PRIX64 " ilo-%u ils-%u ms-%u ehc-%u opt-%08X rk-%u args-%s locals-%s excp-%08X",
            key.A, key.B, value.result, value.info.ftn, value.info.scope, value.info.ILCode_offset, value.info.ILCodeSize,
            value.info.maxStack, value.info.EHcount, value.info.options, value.info.regionKind,
            SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.info.args, GetMethodInfo, SigInstHandleMap).c_str(),
            SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value.info.locals, GetMethodInfo, SigInstHandleMap).c_str(),
            value.exceptionCode);
    }
    else
    {
        printf("GetMethodInfo key ftn-%016" PRIX64 " ctx-%016" PRIX64 ", value res-%u excp-%08X",
            key.A, key.B, value.result, value.exceptionCode);
    }
}
bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO* info, CORINFO_CONTEXT_HANDLE context, DWORD* exceptionCode)
{
    DLDL key;
    key.A = CastHandle(ftn);
    key.B = CastHandle(context);
    Agnostic_GetMethodInfo value = LookupByKeyOrMiss(GetMethodInfo, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetMethodInfo(key, value));

    if (value.result)
    {
        info->ftn        = (CORINFO_METHOD_HANDLE)value.info.ftn;
        info->scope      = (CORINFO_MODULE_HANDLE)value.info.scope;
        info->ILCode     = GetMethodInfo->GetBuffer(value.info.ILCode_offset);
        info->ILCodeSize = (unsigned)value.info.ILCodeSize;
        info->maxStack   = (unsigned)value.info.maxStack;
        info->EHcount    = (unsigned)value.info.EHcount;
        info->options    = (CorInfoOptions)value.info.options;
        info->regionKind = (CorInfoRegionKind)value.info.regionKind;

        info->args   = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.info.args, GetMethodInfo, SigInstHandleMap);
        info->locals = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value.info.locals, GetMethodInfo, SigInstHandleMap);
    }
    bool result    = value.result;
    *exceptionCode = (DWORD)value.exceptionCode;
    return result;
}

void MethodContext::recHaveSameMethodDefinition(CORINFO_METHOD_HANDLE methHnd1, CORINFO_METHOD_HANDLE methHnd2, bool result)
{
    if (HaveSameMethodDefinition == nullptr)
        HaveSameMethodDefinition = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    key.A = CastHandle(methHnd1);
    key.B = CastHandle(methHnd2);

    DWORD value = result ? 1 : 0;
    HaveSameMethodDefinition->Add(key, value);
}
void MethodContext::dmpHaveSameMethodDefinition(const DLDL& key, DWORD value)
{
    printf("HaveSameMethodDefinition key methHnd1-%016" PRIX64 ", methHnd2-%016" PRIX64 ", value %u", key.A, key.B, value);
}
bool MethodContext::repHaveSameMethodDefinition(CORINFO_METHOD_HANDLE methHnd1, CORINFO_METHOD_HANDLE methHnd2)
{
    DLDL key;
    key.A = CastHandle(methHnd1);
    key.B = CastHandle(methHnd2);

    DWORD value = LookupByKeyOrMiss(HaveSameMethodDefinition, key, "key %016" PRIX64 ", %016" PRIX64, key.A, key.B);
    return value != 0;
}

void MethodContext::recGetNewHelper(CORINFO_CLASS_HANDLE  classHandle,
                                    bool                  hasSideEffects,
                                    CorInfoHelpFunc       result,
                                    DWORD                 exceptionCode)
{
    if (GetNewHelper == nullptr)
        GetNewHelper = new LightWeightMap<DWORDLONG, DDD>();

    DWORDLONG key = CastHandle(classHandle);

    DDD value;
    value.A = hasSideEffects ? 1 : 0;
    value.B = (DWORD)result;
    value.C = exceptionCode;

    GetNewHelper->Add(key, value);
    DEBUG_REC(dmpGetNewHelper(key, value));
}
void MethodContext::dmpGetNewHelper(DWORDLONG key, const DDD& value)
{
    printf("GetNewHelper key %016" PRIX64 ", hasSideEffects-%u, value res-%u, exceptionCode-%08X", key, value.A, value.B, value.C);
}
CorInfoHelpFunc MethodContext::repGetNewHelper(CORINFO_CLASS_HANDLE  classHandle,
                                               bool*                 pHasSideEffects,
                                               DWORD*                exceptionCode)
{
    DWORDLONG key = CastHandle(classHandle);

    DDD value = LookupByKeyOrMiss(GetNewHelper, key, ": key %016" PRIX64, key);
    DEBUG_REP(dmpGetNewHelper(key, value));

    if (pHasSideEffects != nullptr)
    {
        *pHasSideEffects = value.A != 0;
    }
    CorInfoHelpFunc result = (CorInfoHelpFunc)value.B;
    *exceptionCode = value.C;
    return result;
}

void MethodContext::recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN*       pResolvedToken,
                                          bool                          fEmbedParent,
                                          CORINFO_METHOD_HANDLE         callerHandle,
                                          CORINFO_GENERICHANDLE_RESULT* pResult)
{
    if (EmbedGenericHandle == nullptr)
        EmbedGenericHandle = new LightWeightMap<Agnostic_EmbedGenericHandle, Agnostic_CORINFO_GENERICHANDLE_RESULT>();

    Agnostic_EmbedGenericHandle key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, EmbedGenericHandle);
    key.fEmbedParent  = (DWORD)fEmbedParent;
    key.hCallerHandle = CastHandle(callerHandle);

    Agnostic_CORINFO_GENERICHANDLE_RESULT value;
    value.lookup            = SpmiRecordsHelper::StoreAgnostic_CORINFO_LOOKUP(&pResult->lookup);
    value.compileTimeHandle = CastHandle(pResult->compileTimeHandle);
    value.handleType        = (DWORD)pResult->handleType;

    EmbedGenericHandle->Add(key, value);
    DEBUG_REC(dmpEmbedGenericHandle(key, value));
}
void MethodContext::dmpEmbedGenericHandle(const Agnostic_EmbedGenericHandle&           key,
                                          const Agnostic_CORINFO_GENERICHANDLE_RESULT& value)
{
    printf("EmbedGenericHandle key rt{%s} emb-%u, value %s cth-%016" PRIX64 " ht-%u",
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ResolvedToken).c_str(),
        key.fEmbedParent,
        SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP(value.lookup).c_str(),
        value.compileTimeHandle,
        value.handleType);
}
void MethodContext::repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN*       pResolvedToken,
                                          bool                          fEmbedParent,
                                          CORINFO_METHOD_HANDLE         callerHandle,
                                          CORINFO_GENERICHANDLE_RESULT* pResult)
{
    AssertMapExistsNoMessage(EmbedGenericHandle);

    Agnostic_EmbedGenericHandle key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, EmbedGenericHandle);
    key.fEmbedParent  = (DWORD)fEmbedParent;
    key.hCallerHandle = CastHandle(callerHandle);

    Agnostic_CORINFO_GENERICHANDLE_RESULT value = LookupByKeyOrMissNoMessage(EmbedGenericHandle, key);

    DEBUG_REP(dmpEmbedGenericHandle(key, value));

    pResult->lookup            = SpmiRecordsHelper::RestoreCORINFO_LOOKUP(value.lookup);
    pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)value.compileTimeHandle;
    pResult->handleType        = (CorInfoGenericHandleType)value.handleType;
}

void MethodContext::recGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE* clause)
{
    if (GetEHinfo == nullptr)
        GetEHinfo = new LightWeightMap<DLD, Agnostic_CORINFO_EH_CLAUSE>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(ftn);
    key.B = (DWORD)EHnumber;

    Agnostic_CORINFO_EH_CLAUSE value;
    value.Flags         = (DWORD)clause->Flags;
    value.TryOffset     = (DWORD)clause->TryOffset;
    value.TryLength     = (DWORD)clause->TryLength;
    value.HandlerOffset = (DWORD)clause->HandlerOffset;
    value.HandlerLength = (DWORD)clause->HandlerLength;
    value.ClassToken    = (DWORD)clause->ClassToken;

    GetEHinfo->Add(key, value);
    DEBUG_REC(dmpGetEHinfo(key, value));
}
void MethodContext::dmpGetEHinfo(DLD key, const Agnostic_CORINFO_EH_CLAUSE& value)
{
    printf("GetEHinfo key ftn-%016" PRIX64 " ehn-%u, value flg-%u to-%u tl-%u ho-%u hl-%u ct-%u", key.A, key.B, value.Flags,
           value.TryOffset, value.TryLength, value.HandlerOffset, value.HandlerLength, value.ClassToken);
}
void MethodContext::repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE* clause)
{
    AssertMapExists(GetEHinfo, ": key %016" PRIX64 "", CastHandle(ftn));

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(ftn);
    key.B = (DWORD)EHnumber;

    Agnostic_CORINFO_EH_CLAUSE value = LookupByKeyOrMiss(GetEHinfo, key, ": key %016" PRIX64 "", CastHandle(ftn));

    DEBUG_REP(dmpGetEHinfo(key, value));

    clause->Flags         = (CORINFO_EH_CLAUSE_FLAGS)value.Flags;
    clause->TryOffset     = (DWORD)value.TryOffset;
    clause->TryLength     = (DWORD)value.TryLength;
    clause->HandlerOffset = (DWORD)value.HandlerOffset;
    clause->HandlerLength = (DWORD)value.HandlerLength;
    clause->ClassToken    = (DWORD)value.ClassToken;
}

void MethodContext::recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
                                             unsigned*             offsetOfIndirection,
                                             unsigned*             offsetAfterIndirection,
                                             bool*                 isRelative)
{
    if (GetMethodVTableOffset == nullptr)
        GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DDD>();

    DDD value;
    value.A = (DWORD)*offsetOfIndirection;
    value.B = (DWORD)*offsetAfterIndirection;
    value.C = *isRelative ? 1 : 0;

    DWORDLONG key = CastHandle(method);
    GetMethodVTableOffset->Add(key, value);
    DEBUG_REC(dmpGetMethodVTableOffset(key, value));
}
void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DDD value)
{
    printf("GetMethodVTableOffset key ftn-%016" PRIX64 ", value offi-%u, offa-%u. offr-%d", key, value.A, value.B, value.C);
}
void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
                                             unsigned*             offsetOfIndirection,
                                             unsigned*             offsetAfterIndirection,
                                             bool*                 isRelative)
{
    DWORDLONG key = CastHandle(method);
    DDD value = LookupByKeyOrMiss(GetMethodVTableOffset, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetMethodVTableOffset(key, value));

    *offsetOfIndirection    = (unsigned)value.A;
    *offsetAfterIndirection = (unsigned)value.B;
    *isRelative             = (value.C != 0);
}

void MethodContext::recResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info, bool returnValue)
{
    if (ResolveVirtualMethod == nullptr)
    {
        ResolveVirtualMethod = new LightWeightMap<Agnostic_ResolveVirtualMethodKey, Agnostic_ResolveVirtualMethodResult>();
    }

    Agnostic_ResolveVirtualMethodKey key;
    ZeroMemory(&key, sizeof(key)); // Zero token including any struct padding
    key.virtualMethod  = CastHandle(info->virtualMethod);
    key.objClass       = CastHandle(info->objClass);
    key.context        = CastHandle(info->context);

    key.pResolvedTokenVirtualMethodNonNull = info->pResolvedTokenVirtualMethod != NULL ? 1 : 0;
    if (key.pResolvedTokenVirtualMethodNonNull)
        key.pResolvedTokenVirtualMethod = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(info->pResolvedTokenVirtualMethod, ResolveToken);

    Agnostic_ResolveVirtualMethodResult result;
    result.returnValue                = returnValue;
    result.devirtualizedMethod        = CastHandle(info->devirtualizedMethod);
    result.requiresInstMethodTableArg = info->requiresInstMethodTableArg;
    result.exactContext               = CastHandle(info->exactContext);
    result.detail                     = (DWORD) info->detail;

    if (returnValue)
    {
        result.resolvedTokenDevirtualizedMethod        = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(&info->resolvedTokenDevirtualizedMethod, ResolveToken);
        result.resolvedTokenDevirtualizedUnboxedMethod = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(&info->resolvedTokenDevirtualizedUnboxedMethod, ResolveToken);
    }
    else
    {
        ZeroMemory(&result.resolvedTokenDevirtualizedMethod, sizeof(result.resolvedTokenDevirtualizedMethod));
        ZeroMemory(&result.resolvedTokenDevirtualizedUnboxedMethod, sizeof(result.resolvedTokenDevirtualizedUnboxedMethod));
    }

    ResolveVirtualMethod->Add(key, result);
    DEBUG_REC(dmpResolveVirtualMethod(key, result));
}

void MethodContext::dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethodKey& key, const Agnostic_ResolveVirtualMethodResult& result)
{
    printf("ResolveVirtualMethod key virtMethod-%016" PRIX64 ", objClass-%016" PRIX64 ", context-%016" PRIX64 " pResolvedTokenVirtualMethodNonNull-%08X pResolvedTokenVirtualMethod{%s}",
        key.virtualMethod,
        key.objClass,
        key.context,
        key.pResolvedTokenVirtualMethodNonNull,
        key.pResolvedTokenVirtualMethodNonNull ? SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.pResolvedTokenVirtualMethod).c_str() : "???");
    printf(", value returnValue-%s, devirtMethod-%016" PRIX64 ", requiresInstArg-%s, exactContext-%016" PRIX64 ", detail-%d, tokDvMeth{%s}, tokDvUnboxMeth{%s}",
        result.returnValue ? "true" : "false",
        result.devirtualizedMethod,
        result.requiresInstMethodTableArg ? "true" : "false",
        result.exactContext,
        result.detail,
        result.returnValue ? SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(result.resolvedTokenDevirtualizedMethod).c_str() : "???",
        result.returnValue ? SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(result.resolvedTokenDevirtualizedUnboxedMethod).c_str() : "???");
}

bool MethodContext::repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info)
{
    Agnostic_ResolveVirtualMethodKey key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.virtualMethod  = CastHandle(info->virtualMethod);
    key.objClass       = CastHandle(info->objClass);
    key.context        = CastHandle(info->context);

    key.pResolvedTokenVirtualMethodNonNull = info->pResolvedTokenVirtualMethod != NULL ? 1 : 0;
    if (key.pResolvedTokenVirtualMethodNonNull)
        key.pResolvedTokenVirtualMethod = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(info->pResolvedTokenVirtualMethod, ResolveToken);

    Agnostic_ResolveVirtualMethodResult result = LookupByKeyOrMiss(ResolveVirtualMethod, key, ": %016" PRIX64 "-%016" PRIX64 "-%016" PRIX64 "-%08X", key.virtualMethod, key.objClass, key.context, key.pResolvedTokenVirtualMethodNonNull);

    DEBUG_REP(dmpResolveVirtualMethod(key, result));

    info->devirtualizedMethod = (CORINFO_METHOD_HANDLE) result.devirtualizedMethod;
    info->requiresInstMethodTableArg = result.requiresInstMethodTableArg;
    info->exactContext = (CORINFO_CONTEXT_HANDLE) result.exactContext;
    info->detail = (CORINFO_DEVIRTUALIZATION_DETAIL) result.detail;
    if (result.returnValue)
    {
        info->resolvedTokenDevirtualizedMethod = SpmiRecordsHelper::Restore_CORINFO_RESOLVED_TOKEN(&result.resolvedTokenDevirtualizedMethod, ResolveToken);
        info->resolvedTokenDevirtualizedUnboxedMethod = SpmiRecordsHelper::Restore_CORINFO_RESOLVED_TOKEN(&result.resolvedTokenDevirtualizedUnboxedMethod, ResolveToken);
    }
    return result.returnValue;
}

void MethodContext::recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
                                       bool*                 requiresInstMethodTableArg,
                                       CORINFO_METHOD_HANDLE result)
{
    if (GetUnboxedEntry == nullptr)
    {
        GetUnboxedEntry = new LightWeightMap<DWORDLONG, DLD>();
    }

    DWORDLONG key = CastHandle(ftn);
    DLD       value;
    value.A = CastHandle(result);
    if (requiresInstMethodTableArg != nullptr)
    {
        value.B = (DWORD)*requiresInstMethodTableArg ? 1 : 0;
    }
    else
    {
        value.B = 0;
    }
    GetUnboxedEntry->Add(key, value);
    DEBUG_REC(dmpGetUnboxedEntry(key, value));
}

void MethodContext::dmpGetUnboxedEntry(DWORDLONG key, DLD value)
{
    printf("GetUnboxedEntry ftn-%016" PRIX64 ", result-%016" PRIX64 ", requires-inst-%u", key, value.A, value.B);
}

CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
{
    DWORDLONG key = CastHandle(ftn);

    DLD value = LookupByKeyOrMiss(GetUnboxedEntry, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetUnboxedEntry(key, value));

    if (requiresInstMethodTableArg != nullptr)
    {
        *requiresInstMethodTableArg = (value.B == 1);
    }
    return (CORINFO_METHOD_HANDLE)(value.A);
}

void MethodContext::recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
    if (GetDefaultComparerClass == nullptr)
        GetDefaultComparerClass = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = CastHandle(result);
    GetDefaultComparerClass->Add(key, value);
    DEBUG_REC(dmpGetDefaultComparerClass(key, value));
}
void MethodContext::dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value)
{
    printf("GetDefaultComparerClass key cls-%016" PRIX64 ", value cls-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetDefaultComparerClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetDefaultComparerClass(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
    if (GetDefaultEqualityComparerClass == nullptr)
        GetDefaultEqualityComparerClass = new LightWeightMap<DWORDLONG, DWORDLONG>();

    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = CastHandle(result);
    GetDefaultEqualityComparerClass->Add(key, value);
    DEBUG_REC(dmpGetDefaultEqualityComparerClass(key, value));
}
void MethodContext::dmpGetDefaultEqualityComparerClass(DWORDLONG key, DWORDLONG value)
{
    printf("GetDefaultEqualityComparerClass key cls-%016" PRIX64 ", value cls-%016" PRIX64 "", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORDLONG value = LookupByKeyOrMiss(GetDefaultEqualityComparerClass, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetDefaultEqualityComparerClass(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CLASS_HANDLE result)
{
    if (GetTokenTypeAsHandle == nullptr)
        GetTokenTypeAsHandle = new LightWeightMap<GetTokenTypeAsHandleValue, DWORDLONG>();

    GetTokenTypeAsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hMethod = CastHandle(pResolvedToken->hMethod);
    key.hField  = CastHandle(pResolvedToken->hField);

    DWORDLONG value = CastHandle(result);
    GetTokenTypeAsHandle->Add(key, value);
    DEBUG_REC(dmpGetTokenTypeAsHandle(key, value));
}
void MethodContext::dmpGetTokenTypeAsHandle(const GetTokenTypeAsHandleValue& key, DWORDLONG value)
{
    printf("GetTokenTypeAsHandle key ftn-%016" PRIX64 " fld-%016" PRIX64 ", value cls-%016" PRIX64 "", key.hMethod, key.hField, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetTokenTypeAsHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken)
{
    GetTokenTypeAsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hMethod = CastHandle(pResolvedToken->hMethod);
    key.hField  = CastHandle(pResolvedToken->hField);

    DWORDLONG value = LookupByKeyOrMissNoMessage(GetTokenTypeAsHandle, key);

    DEBUG_REP(dmpGetTokenTypeAsHandle(key, value));
    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value;
    return result;
}

void MethodContext::recGetFieldInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                    CORINFO_METHOD_HANDLE   callerHandle,
                                    CORINFO_ACCESS_FLAGS    flags,
                                    CORINFO_FIELD_INFO*     pResult)
{
    if (GetFieldInfo == nullptr)
        GetFieldInfo = new LightWeightMap<Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO>();

    Agnostic_GetFieldInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetFieldInfo);
    key.callerHandle  = CastHandle(callerHandle);
    key.flags         = (DWORD)flags;

    Agnostic_CORINFO_FIELD_INFO value;
    value.fieldAccessor                 = (DWORD)pResult->fieldAccessor;
    value.fieldFlags                    = (DWORD)pResult->fieldFlags;
    value.helper                        = (DWORD)pResult->helper;
    value.offset                        = (DWORD)pResult->offset;
    value.fieldType                     = (DWORD)pResult->fieldType;
    value.structType                    = CastHandle(pResult->structType);
    value.accessAllowed                 = (DWORD)pResult->accessAllowed;
    value.accessCalloutHelper.helperNum = (DWORD)pResult->accessCalloutHelper.helperNum;
    value.accessCalloutHelper.numArgs   = (DWORD)pResult->accessCalloutHelper.numArgs;
    value.fieldLookup                   = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pResult->fieldLookup);
    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        value.accessCalloutHelper.args[i].constant = (DWORDLONG)pResult->accessCalloutHelper.args[i].constant;
        value.accessCalloutHelper.args[i].argType  = (DWORD)pResult->accessCalloutHelper.args[i].argType;
    }
    GetFieldInfo->Add(key, value);
    DEBUG_REC(dmpGetFieldInfo(key, value));
}
void MethodContext::dmpGetFieldInfo(const Agnostic_GetFieldInfo& key, const Agnostic_CORINFO_FIELD_INFO& value)
{
    printf("GetFieldInfo key ch-%016" PRIX64 " flg-%08X rt{%s}", key.callerHandle, key.flags,
           SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ResolvedToken).c_str());

    printf(", value fa-%u fflg-%08X hlp-%u off-%u fT-%u(%s) sT-%016" PRIX64 " aa-%u hnum-%u na-%u {", value.fieldAccessor,
           value.fieldFlags, value.helper, value.offset, value.fieldType, toString((CorInfoType)value.fieldType),
           value.structType, value.accessAllowed, value.accessCalloutHelper.helperNum,
           value.accessCalloutHelper.numArgs);

    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        switch ((CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType)
        {
            default:
                printf("{%u: illegal}", i);
                break;
            case CORINFO_HELPER_ARG_TYPE_Field:
                printf("{%u: fld-%016" PRIX64 "}", i, value.accessCalloutHelper.args[i].constant);
                break;
            case CORINFO_HELPER_ARG_TYPE_Method:
                printf("{%u: mth-%016" PRIX64 "}", i, value.accessCalloutHelper.args[i].constant);
                break;
            case CORINFO_HELPER_ARG_TYPE_Class:
                printf("{%u: cls-%016" PRIX64 "}", i, value.accessCalloutHelper.args[i].constant);
                break;
            case CORINFO_HELPER_ARG_TYPE_Module:
                printf("{%u: mod-%016" PRIX64 "}", i, value.accessCalloutHelper.args[i].constant);
                break;
            case CORINFO_HELPER_ARG_TYPE_Const:
                printf("{%u: const-%016" PRIX64 "}", i, value.accessCalloutHelper.args[i].constant);
                break;
        }
    }
    printf(" fl %s}", SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.fieldLookup).c_str());
}
void MethodContext::repGetFieldInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                    CORINFO_METHOD_HANDLE   callerHandle,
                                    CORINFO_ACCESS_FLAGS    flags,
                                    CORINFO_FIELD_INFO*     pResult)
{
    AssertMapExists(GetFieldInfo, ": key %x", pResolvedToken->token);

    Agnostic_GetFieldInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, GetFieldInfo);
    key.callerHandle  = CastHandle(callerHandle);
    key.flags         = (DWORD)flags;

    DWORD origFlag = key.flags;

    int keyIndex = GetFieldInfo->GetIndex(key);
    if (keyIndex == -1)
    {
#ifdef sparseMC
        key.flags = origFlag ^ ((DWORD)CORINFO_ACCESS_THIS);
        keyIndex = GetFieldInfo->GetIndex(key);
        if (keyIndex != -1)
        {
            LogDebug(
                "Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_THIS");
        }
        else
        {
            key.flags = origFlag ^ (DWORD)CORINFO_ACCESS_INLINECHECK;
            keyIndex = GetFieldInfo->GetIndex(key);
            if (keyIndex != -1)
            {
                LogDebug("Sparse - repGetFieldInfo found value with inverted CORINFO_ACCESS_INLINECHECK");
            }
            else
            {
                LogException(EXCEPTIONCODE_MC, "repGetFieldInfo: didn't find %x", pResolvedToken->token);
            }
        }
#else
        LogException(EXCEPTIONCODE_MC, "repGetFieldInfo: didn't find %x", pResolvedToken->token);
#endif
    }

    Agnostic_CORINFO_FIELD_INFO value = GetFieldInfo->GetItem(keyIndex);
    DEBUG_REP(dmpGetFieldInfo(key, value));

    pResult->fieldAccessor                 = (CORINFO_FIELD_ACCESSOR)value.fieldAccessor;
    pResult->fieldFlags                    = (unsigned)value.fieldFlags;
    pResult->helper                        = (CorInfoHelpFunc)value.helper;
    pResult->offset                        = (DWORD)value.offset;
    pResult->fieldType                     = (CorInfoType)value.fieldType;
    pResult->structType                    = (CORINFO_CLASS_HANDLE)value.structType;
    pResult->accessAllowed                 = (CorInfoIsAccessAllowedResult)value.accessAllowed;
    pResult->accessCalloutHelper.helperNum = (CorInfoHelpFunc)value.accessCalloutHelper.helperNum;
    pResult->accessCalloutHelper.numArgs   = (unsigned)value.accessCalloutHelper.numArgs;
    pResult->fieldLookup                   = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.fieldLookup);
    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        pResult->accessCalloutHelper.args[i].constant = (size_t)value.accessCalloutHelper.args[i].constant;
        pResult->accessCalloutHelper.args[i].argType =
            (CorInfoAccessAllowedHelperArgType)value.accessCalloutHelper.args[i].argType;
    }
}

void MethodContext::recGetThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, bool isGCType, uint32_t result)
{
    if (GetThreadLocalFieldInfo == nullptr)
        GetThreadLocalFieldInfo = new LightWeightMap<DLD, DWORD>();

    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(field);
    key.B = isGCType ? 0 : 1;
    GetThreadLocalFieldInfo->Add(key, result);
    DEBUG_REC(dmpGetThreadLocalFieldInfo(key, result));
}

void MethodContext::dmpGetThreadLocalFieldInfo(DLD key, DWORD value)
{
    printf("GetThreadLocalFieldInfo key hnd-%016" PRIX64 ",gctype-%d result-%u", key.A, key.B, value);
}

uint32_t MethodContext::repGetThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, bool isGCType)
{
    DLD key;
    ZeroMemory(&key, sizeof(key));

    key.A           = CastHandle(field);
    key.B           = isGCType ? 0 : 1;
    DWORD     value = LookupByKeyOrMiss(GetThreadLocalFieldInfo, key, ": key hnd-%016" PRIX64 ", gctype-%u", key.A, key.B);

    DEBUG_REP(dmpGetThreadLocalFieldInfo(key, value));

    return value;
}

void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType)
{
    if (GetThreadLocalStaticBlocksInfo == nullptr)
        GetThreadLocalStaticBlocksInfo = new LightWeightMap<DWORD, Agnostic_GetThreadLocalStaticBlocksInfo>();

    Agnostic_GetThreadLocalStaticBlocksInfo value;
    ZeroMemory(&value, sizeof(value));

    value.tlsIndex                              = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsIndex);
    value.tlsGetAddrFtnPtr                      = CastPointer(pInfo->tlsGetAddrFtnPtr);
    value.tlsIndexObject                        = CastPointer(pInfo->tlsIndexObject);
    value.threadVarsSection                     = CastPointer(pInfo->threadVarsSection);
    value.offsetOfThreadLocalStoragePointer     = pInfo->offsetOfThreadLocalStoragePointer;
    value.offsetOfMaxThreadStaticBlocks         = pInfo->offsetOfMaxThreadStaticBlocks;
    value.offsetOfThreadStaticBlocks            = pInfo->offsetOfThreadStaticBlocks;
    value.offsetOfGCDataPointer                 = pInfo->offsetOfGCDataPointer;

    // This data is same for entire process, so just add it against key '0'.
    DWORD key = isGCType ? 0 : 1;
    GetThreadLocalStaticBlocksInfo->Add(key, value);
    DEBUG_REC(dmpGetThreadLocalStaticBlocksInfo(key, value));
}

void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_GetThreadLocalStaticBlocksInfo& value)
{
    printf("GetThreadLocalStaticBlocksInfo key %u, tlsIndex-%s, "
           ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 
           ", threadVarsSection - %016" PRIX64
           ", offsetOfThreadLocalStoragePointer-%u, offsetOfMaxThreadStaticBlocks-%u"
           ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u",           
           key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr,
           value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer,
           value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfGCDataPointer);
}

void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType)
{
    int key = isGCType ? 0 : 1;
    Agnostic_GetThreadLocalStaticBlocksInfo value = LookupByKeyOrMiss(GetThreadLocalStaticBlocksInfo, key, ": key %u", key);

    DEBUG_REP(dmpGetThreadLocalStaticBlocksInfo(key, value));

    pInfo->tlsIndex                             = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsIndex);
    pInfo->tlsGetAddrFtnPtr                     = (void*)value.tlsGetAddrFtnPtr;
    pInfo->tlsIndexObject                       = (void*)value.tlsIndexObject;
    pInfo->threadVarsSection                    = (void*)value.threadVarsSection;
    pInfo->offsetOfThreadLocalStoragePointer    = value.offsetOfThreadLocalStoragePointer;
    pInfo->offsetOfMaxThreadStaticBlocks        = value.offsetOfMaxThreadStaticBlocks;    
    pInfo->offsetOfThreadStaticBlocks           = value.offsetOfThreadStaticBlocks;
    pInfo->offsetOfGCDataPointer                = value.offsetOfGCDataPointer;
}

void MethodContext::recGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo)
{
    if (GetThreadLocalStaticInfo_NativeAOT == nullptr)
        GetThreadLocalStaticInfo_NativeAOT = new LightWeightMap<DWORDLONG, Agnostic_GetThreadStaticInfo_NativeAOT>();

    Agnostic_GetThreadStaticInfo_NativeAOT value;
    ZeroMemory(&value, sizeof(value));
    value.tlsRootObject       = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsRootObject);
    value.tlsIndexObject      = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsIndexObject);
    value.offsetOfThreadLocalStoragePointer       = pInfo->offsetOfThreadLocalStoragePointer;
    value.threadStaticBaseSlow       = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->threadStaticBaseSlow);
    value.tlsGetAddrFtnPtr           = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsGetAddrFtnPtr);

    DWORDLONG key = 1;

    GetThreadLocalStaticInfo_NativeAOT->Add(key, value);
    DEBUG_REC(dmpGetThreadLocalStaticInfo_NativeAOT(key, result));
}
void MethodContext::dmpGetThreadLocalStaticInfo_NativeAOT(DWORDLONG                                     key,
                                                          const Agnostic_GetThreadStaticInfo_NativeAOT& value)
{
    printf("GetThreadLocalStaticInfo_NativeAOT key %016" PRIX64 ", tlsRootObject-%s, tlsIndexObject-%s,  offsetOfThreadLocalStoragePointer-%u, "
           "threadStaticBaseSlow-%s, tlsGetAddrFtnPtr-%s",
           key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsRootObject).c_str(),
           SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndexObject).c_str(),
           value.offsetOfThreadLocalStoragePointer,
           SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.threadStaticBaseSlow).c_str(),
           SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsGetAddrFtnPtr).c_str());
}

void MethodContext::repGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo)
{
    DWORDLONG                              key = 1;
    Agnostic_GetThreadStaticInfo_NativeAOT value =
        LookupByKeyOrMiss(GetThreadLocalStaticInfo_NativeAOT, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetThreadLocalStaticInfo_NativeAOT(key, value));

    pInfo->tlsRootObject                     = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsRootObject);
    pInfo->tlsIndexObject                    = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsIndexObject);
    pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer;
    pInfo->threadStaticBaseSlow              = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.threadStaticBaseSlow);
    pInfo->tlsGetAddrFtnPtr                  = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsGetAddrFtnPtr);
}

void MethodContext::recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle,
                                         void**                ppIndirection,
                                         CORINFO_METHOD_HANDLE result)
{
    if (EmbedMethodHandle == nullptr)
        EmbedMethodHandle = new LightWeightMap<DWORDLONG, DLDL>();

    DLDL value;
    if (ppIndirection == nullptr)
        value.A = 0;
    else
        value.A = CastPointer(*ppIndirection);
    value.B     = CastHandle(result);

    DWORDLONG key = CastHandle(handle);
    EmbedMethodHandle->Add(key, value);
    DEBUG_REC(dmpEmbedMethodHandle(key, value));
}
void MethodContext::dmpEmbedMethodHandle(DWORDLONG key, DLDL value)
{
    printf("EmbedMethodHandle key ftn-%016" PRIX64 ", value pp-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
CORINFO_METHOD_HANDLE MethodContext::repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void** ppIndirection)
{
    DWORDLONG key = CastHandle(handle);
    DLDL value = LookupByKeyOrMiss(EmbedMethodHandle, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpEmbedMethodHandle(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_METHOD_HANDLE)value.B;
}

void MethodContext::recGetStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects, bool result)
{
    if (GetStaticFieldContent == nullptr)
        GetStaticFieldContent = new LightWeightMap<DLDDD, DD>();

    DLDDD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(field);
    key.B = (DWORD)bufferSize;
    key.C = (DWORD)ignoreMovableObjects;
    key.D = (DWORD)valueOffset;

    DWORD tmpBuf = (DWORD)-1;
    if (buffer != nullptr && result)
        tmpBuf = (DWORD)GetStaticFieldContent->AddBuffer((uint8_t*)buffer, (uint32_t)bufferSize);

    DD value;
    value.A = (DWORD)result;
    value.B = (DWORD)tmpBuf;

    GetStaticFieldContent->Add(key, value);
    DEBUG_REC(dmpGetStaticFieldContent(key, value));
}
void MethodContext::dmpGetStaticFieldContent(DLDDD key, DD value)
{
    printf("GetStaticFieldContent key fld-%016" PRIX64 " bufSize-%u, ignoremovable-%u, valOffset-%u result-%u", key.A, key.B, key.C, key.D, value.A);
    GetStaticFieldContent->Unlock();
}
bool MethodContext::repGetStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects)
{
    DLDDD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(field);
    key.B = (DWORD)bufferSize;
    key.C = (DWORD)ignoreMovableObjects;
    key.D = (DWORD)valueOffset;

    DD value = LookupByKeyOrMiss(GetStaticFieldContent, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetStaticFieldContent(key, value));
    if (buffer != nullptr && (bool)value.A)
    {
        uint8_t* srcBuffer = (uint8_t*)GetStaticFieldContent->GetBuffer(value.B);
        Assert(srcBuffer != nullptr);
        memcpy(buffer, srcBuffer, bufferSize);
    }
    return (bool)value.A;
}

void MethodContext::recGetObjectContent(CORINFO_OBJECT_HANDLE obj, uint8_t* buffer, int bufferSize, int valueOffset, bool result)
{
    if (GetObjectContent == nullptr)
        GetObjectContent = new LightWeightMap<DLDD, DD>();

    DLDD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(obj);
    key.B = (DWORD)bufferSize;
    key.C = (DWORD)valueOffset;

    DWORD tmpBuf = (DWORD)-1;
    if (buffer != nullptr && result)
        tmpBuf = (DWORD)GetObjectContent->AddBuffer((uint8_t*)buffer, (uint32_t)bufferSize);

    DD value;
    value.A = (DWORD)result;
    value.B = (DWORD)tmpBuf;

    GetObjectContent->Add(key, value);
    DEBUG_REC(dmpGetObjectContent(key, value));
}
void MethodContext::dmpGetObjectContent(DLDD key, DD value)
{
    printf("GetObjectContent key fld-%016" PRIX64 " bufSize-%u, valOffset-%u result-%u", key.A, key.B, key.C, value.A);
    GetObjectContent->Unlock();
}
bool MethodContext::repGetObjectContent(CORINFO_OBJECT_HANDLE obj, uint8_t* buffer, int bufferSize, int valueOffset)
{
    DLDD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(obj);
    key.B = (DWORD)bufferSize;
    key.C = (DWORD)valueOffset;

    DD value = LookupByKeyOrMiss(GetObjectContent, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetObjectContent(key, value));
    if (buffer != nullptr && (bool)value.A)
    {
        uint8_t* srcBuffer = (uint8_t*)GetObjectContent->GetBuffer(value.B);
        Assert(srcBuffer != nullptr);
        memcpy(buffer, srcBuffer, bufferSize);
    }
    return (bool)value.A;
}

void MethodContext::recGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field,
                                                  bool*                pIsSpeculative,
                                                  CORINFO_CLASS_HANDLE result)
{
    if (GetStaticFieldCurrentClass == nullptr)
        GetStaticFieldCurrentClass = new LightWeightMap<DLD, Agnostic_GetStaticFieldCurrentClass>();

    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(field);
    key.B = pIsSpeculative != nullptr ? 1 : 0;

    Agnostic_GetStaticFieldCurrentClass value;
    value.classHandle   = CastHandle(result);
    value.isSpeculative = pIsSpeculative != nullptr ? *pIsSpeculative : false;

    GetStaticFieldCurrentClass->Add(key, value);
    DEBUG_REC(dmpGetStaticFieldCurrentClass(key, value));
}
void MethodContext::dmpGetStaticFieldCurrentClass(DLD key, const Agnostic_GetStaticFieldCurrentClass& value)
{
    printf("GetStaticFieldCurrentClass key fld-%016" PRIX64 ", value clsHnd-%016" PRIX64 " isSpeculative-%u", key.A, value.classHandle,
           value.isSpeculative);
}
CORINFO_CLASS_HANDLE MethodContext::repGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool* pIsSpeculative)
{
    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(field);
    key.B = pIsSpeculative != nullptr ? 1 : 0;

    Agnostic_GetStaticFieldCurrentClass value = LookupByKeyOrMiss(GetStaticFieldCurrentClass, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetStaticFieldCurrentClass(key, value));

    if (pIsSpeculative != nullptr)
    {
        *pIsSpeculative = value.isSpeculative;
    }

    CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value.classHandle;
    return result;
}

void MethodContext::recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE* gcPtrs, unsigned len, unsigned result)
{
    if (GetClassGClayout == nullptr)
        GetClassGClayout = new LightWeightMap<DWORDLONG, Agnostic_GetClassGClayout>();

    Agnostic_GetClassGClayout value;

    value.gcPtrs_Index = (DWORD)GetClassGClayout->AddBuffer((unsigned char*)gcPtrs, len * sizeof(BYTE));
    value.len          = (DWORD)len;
    value.valCount     = (DWORD)result;

    DWORDLONG key = CastHandle(cls);
    GetClassGClayout->Add(key, value);
    DEBUG_REC(dmpGetClassGClayout(key, value));
}
void MethodContext::dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value)
{
    printf("GetClassGCLayout key %016" PRIX64 ", value len %u cnt %u {", key, value.len, value.valCount);
    if (value.gcPtrs_Index != (DWORD)-1)
    {
        BYTE* ptr = (BYTE*)GetClassGClayout->GetBuffer(value.gcPtrs_Index);
        for (unsigned int i = 0; i < value.len; i++)
        {
            printf("0x%02x", ptr[i]);
            if (i + 1 < value.len)
                printf(",");
        }
        GetClassGClayout->Unlock();
    }
    printf("}");
}
unsigned MethodContext::repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE* gcPtrs)
{
    DWORDLONG key = CastHandle(cls);
    Agnostic_GetClassGClayout value = LookupByKeyOrMiss(GetClassGClayout, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetClassGClayout(key, value));

    unsigned int len   = (unsigned int)value.len;
    unsigned int index = (unsigned int)value.gcPtrs_Index;

    if (index != (unsigned int)-1)
    {
        BYTE* ptr = (BYTE*)GetClassGClayout->GetBuffer(index);
        for (unsigned int i = 0; i < len; i++)
            gcPtrs[i]       = ptr[i];
    }
    return (unsigned)value.valCount;
}

void MethodContext::recGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, bool fDoubleAlignHint, unsigned result)
{
    if (GetClassAlignmentRequirement == nullptr)
        GetClassAlignmentRequirement = new LightWeightMap<DLD, DWORD>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls);
    key.B = (DWORD)fDoubleAlignHint;

    DWORD value = (DWORD)result;
    GetClassAlignmentRequirement->Add(key, value);
    DEBUG_REC(dmpGetClassAlignmentRequirement(key, value));
}
void MethodContext::dmpGetClassAlignmentRequirement(DLD key, DWORD value)
{
    printf("GetClassAlignmentRequirement key %016" PRIX64 " %u, value %u", key.A, key.B, value);
}
unsigned MethodContext::repGetClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, bool fDoubleAlignHint)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls);
    key.B = (DWORD)fDoubleAlignHint;

    DWORD value = LookupByKeyOrMiss(GetClassAlignmentRequirement, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetClassAlignmentRequirement(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recCanAccessClass(CORINFO_RESOLVED_TOKEN*      pResolvedToken,
                                      CORINFO_METHOD_HANDLE        callerHandle,
                                      CORINFO_HELPER_DESC*         pAccessHelper,
                                      CorInfoIsAccessAllowedResult result)
{
    if (CanAccessClass == nullptr)
        CanAccessClass = new LightWeightMap<Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut>();

    Agnostic_CanAccessClassIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, CanAccessClass);
    key.callerHandle  = CastHandle(callerHandle);

    Agnostic_CanAccessClassOut value;
    value.AccessHelper.helperNum = (DWORD)pAccessHelper->helperNum;
    value.AccessHelper.numArgs   = (DWORD)pAccessHelper->numArgs;
    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        value.AccessHelper.args[i].constant = (DWORDLONG)pAccessHelper->args[i].constant;
        value.AccessHelper.args[i].argType  = (DWORD)pAccessHelper->args[i].argType;
    }
    value.result = (DWORD)result;

    CanAccessClass->Add(key, value);
    DEBUG_REC(dmpCanAccessClass(key, value));
}
void MethodContext::dmpCanAccessClass(const Agnostic_CanAccessClassIn& key, const Agnostic_CanAccessClassOut& value)
{
    printf("CanAccessClass key rt{%s}, callerHandle %016" PRIX64 "\n",
           SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.ResolvedToken).c_str(), key.callerHandle);
    printf(", value hnum-%u na-%u {", value.AccessHelper.helperNum, value.AccessHelper.numArgs);
    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        printf("{%016" PRIX64 " %u}", value.AccessHelper.args[i].constant, value.AccessHelper.args[i].argType);
    }
    printf("} res-%u", value.result);
}
CorInfoIsAccessAllowedResult MethodContext::repCanAccessClass(CORINFO_RESOLVED_TOKEN* pResolvedToken,
                                                              CORINFO_METHOD_HANDLE   callerHandle,
                                                              CORINFO_HELPER_DESC*    pAccessHelper)
{
    AssertMapExists(CanAccessClass, ": key %016" PRIX64 "", CastHandle(pResolvedToken->hClass));

    Agnostic_CanAccessClassIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.ResolvedToken = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pResolvedToken, CanAccessClass);
    key.callerHandle  = CastHandle(callerHandle);

    Agnostic_CanAccessClassOut value = LookupByKeyOrMiss(CanAccessClass, key, ": key %016" PRIX64 "", CastHandle(pResolvedToken->hClass));

    DEBUG_REP(dmpCanAccessClass(key, value));

    pAccessHelper->helperNum = (CorInfoHelpFunc)value.AccessHelper.helperNum;
    pAccessHelper->numArgs   = (unsigned)value.AccessHelper.numArgs;
    for (int i = 0; i < CORINFO_ACCESS_ALLOWED_MAX_ARGS; i++)
    {
        pAccessHelper->args[i].constant = (size_t)value.AccessHelper.args[i].constant;
        pAccessHelper->args[i].argType  = (CorInfoAccessAllowedHelperArgType)value.AccessHelper.args[i].argType;
    }
    CorInfoIsAccessAllowedResult temp = (CorInfoIsAccessAllowedResult)value.result;
    return temp;
}

void MethodContext::recGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing, CorInfoHelpFunc result)
{
    if (GetCastingHelper == nullptr)
        GetCastingHelper = new LightWeightMap<Agnostic_GetCastingHelper, DWORD>();

    Agnostic_GetCastingHelper key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hClass    = CastHandle(pResolvedToken->hClass);
    key.fThrowing = (DWORD)fThrowing;

    DWORD value = (DWORD)result;
    GetCastingHelper->Add(key, value);
    DEBUG_REC(dmpGetCastingHelper(key, value));
}
void MethodContext::dmpGetCastingHelper(const Agnostic_GetCastingHelper& key, DWORD value)
{
    printf("GetCastingHelper key cls-%016" PRIX64 ", thw-%u, value res-%u", key.hClass, key.fThrowing, value);
}
CorInfoHelpFunc MethodContext::repGetCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing)
{
    Agnostic_GetCastingHelper key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hClass    = CastHandle(pResolvedToken->hClass);
    key.fThrowing = (DWORD)fThrowing;

    DWORD value = LookupByKeyOrMiss(GetCastingHelper, key, ": key %016" PRIX64 "", key.hClass);

    DEBUG_REP(dmpGetCastingHelper(key, value));
    CorInfoHelpFunc result = (CorInfoHelpFunc)value;
    return result;
}

void MethodContext::recEmbedModuleHandle(CORINFO_MODULE_HANDLE handle,
                                         void**                ppIndirection,
                                         CORINFO_MODULE_HANDLE result)
{
    if (EmbedModuleHandle == nullptr)
        EmbedModuleHandle = new LightWeightMap<DWORDLONG, DLDL>();

    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastHandle(result);

    DWORDLONG key = CastHandle(handle);
    EmbedModuleHandle->Add(key, value);
    DEBUG_REC(dmpEmbedModuleHandle(key, value));
}
void MethodContext::dmpEmbedModuleHandle(DWORDLONG key, DLDL value)
{
    printf("EmbedModuleHandle key mod-%016" PRIX64 ", value pp-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
CORINFO_MODULE_HANDLE MethodContext::repEmbedModuleHandle(CORINFO_MODULE_HANDLE handle, void** ppIndirection)
{
    DWORDLONG key = CastHandle(handle);
    DLDL value = LookupByKeyOrMiss(EmbedModuleHandle, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpEmbedModuleHandle(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_MODULE_HANDLE)value.B;
}

void MethodContext::recEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void** ppIndirection, CORINFO_CLASS_HANDLE result)
{
    if (EmbedClassHandle == nullptr)
        EmbedClassHandle = new LightWeightMap<DWORDLONG, DLDL>();

    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastHandle(result);

    DWORDLONG key = CastHandle(handle);
    EmbedClassHandle->Add(key, value);
    DEBUG_REC(dmpEmbedClassHandle(key, value));
}
void MethodContext::dmpEmbedClassHandle(DWORDLONG key, DLDL value)
{
    printf("EmbedClassHandle key cls-%016" PRIX64 ", value pp-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
CORINFO_CLASS_HANDLE MethodContext::repEmbedClassHandle(CORINFO_CLASS_HANDLE handle, void** ppIndirection)
{
    DWORDLONG key = CastHandle(handle);
    DLDL value = LookupByKeyOrMiss(EmbedClassHandle, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpEmbedClassHandle(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_CLASS_HANDLE)value.B;
}

void MethodContext::recPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method,
                                                 CORINFO_SIG_INFO*     callSiteSig,
                                                 bool                  result)
{
    if (PInvokeMarshalingRequired == nullptr)
        PInvokeMarshalingRequired = new LightWeightMap<MethodOrSigInfoValue, DWORD>();

    MethodOrSigInfoValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.method     = CastHandle(method);
    key.pSig_Index = (DWORD)PInvokeMarshalingRequired->AddBuffer((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
    key.cbSig      = (DWORD)callSiteSig->cbSig;
    key.scope      = CastHandle(callSiteSig->scope);

    DWORD value = result ? 1 : 0;
    PInvokeMarshalingRequired->Add(key, value);
    DEBUG_REC(dmpPInvokeMarshalingRequired(key, value));
}
void MethodContext::dmpPInvokeMarshalingRequired(const MethodOrSigInfoValue& key, DWORD value)
{
    printf("PInvokeMarshalingRequired key mth-%016" PRIX64 " scp-%016" PRIX64 " sig-%s, value res-%u",
        key.method, key.scope,
        SpmiDumpHelper::DumpPSig(key.pSig_Index, key.cbSig, PInvokeMarshalingRequired).c_str(),
        value);
}
// Note the jit interface implementation seems to only care about scope and pSig from callSiteSig
bool MethodContext::repPInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig)
{
    if (PInvokeMarshalingRequired == nullptr) // so when we replay Checked on Release, we throw from lwm
        return true;                          // TODO-Cleanup: hackish...

    MethodOrSigInfoValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.method     = CastHandle(method);
    key.pSig_Index = (DWORD)PInvokeMarshalingRequired->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
    key.cbSig      = (DWORD)callSiteSig->cbSig;
    key.scope      = CastHandle(callSiteSig->scope);

    DWORD value = LookupByKeyOrMissNoMessage(PInvokeMarshalingRequired, key);

    DEBUG_REP(dmpPInvokeMarshalingRequired(key, value));
    return value != 0;
}

void MethodContext::recGetUnmanagedCallConv(CORINFO_METHOD_HANDLE    method,
                                            CORINFO_SIG_INFO*        callSiteSig,
                                            CorInfoCallConvExtension result,
                                            bool                     suppressGCTransitionResult)
{
    if (GetUnmanagedCallConv == nullptr)
        GetUnmanagedCallConv = new LightWeightMap<MethodOrSigInfoValue, DD>();

    MethodOrSigInfoValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.method = CastHandle(method);
    if (callSiteSig != nullptr)
    {
        key.pSig_Index = (DWORD)GetUnmanagedCallConv->AddBuffer((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
        key.cbSig = (DWORD)callSiteSig->cbSig;
        key.scope = CastHandle(callSiteSig->scope);
    }
    else
    {
        key.pSig_Index = 0;
        key.cbSig = 0;
        key.scope = 0;
    }

    DD value;
    value.A = (DWORD)result;
    value.B = (DWORD)suppressGCTransitionResult;

    GetUnmanagedCallConv->Add(key, value);
    DEBUG_REC(dmpGetUnmanagedCallConv(key, value));
}
void MethodContext::dmpGetUnmanagedCallConv(const MethodOrSigInfoValue& key, DD value)
{
    printf("GetUnmanagedCallConv key mth-%016" PRIX64 " scp-%016" PRIX64 " sig-%s, value res-%u,%u",
        key.method, key.scope,
        SpmiDumpHelper::DumpPSig(key.pSig_Index, key.cbSig, GetUnmanagedCallConv).c_str(),
        value.A, value.B);
}
// Note the jit interface implementation seems to only care about scope and pSig from callSiteSig
CorInfoCallConvExtension MethodContext::repGetUnmanagedCallConv(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig, bool* pSuppressGCTransition)
{
    if (GetUnmanagedCallConv == nullptr)
    {
#ifdef sparseMC
        LogDebug("Sparse - repGetUnmanagedCallConv returning CorInfoCallConvExtension::Managed");
        return CorInfoCallConvExtension::Managed;
#else
        LogException(EXCEPTIONCODE_MC, "Found a null GetUnmanagedCallConv.  Probably missing a fatTrigger for %016" PRIX64 ".",
                     CastHandle(method));
#endif
    }

    MethodOrSigInfoValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.method = CastHandle(method);
    if (callSiteSig != nullptr)
    {
        key.pSig_Index = (DWORD)GetUnmanagedCallConv->Contains((unsigned char*)callSiteSig->pSig, callSiteSig->cbSig);
        key.cbSig = (DWORD)callSiteSig->cbSig;
        key.scope = CastHandle(callSiteSig->scope);
    }
    else
    {
        key.pSig_Index = 0;
        key.cbSig = 0;
        key.scope = 0;
    }

    DD value = LookupByKeyOrMissNoMessage(GetUnmanagedCallConv, key);

    DEBUG_REP(dmpGetUnmanagedCallConv(key, value));

    *pSuppressGCTransition = value.B != 0;
    return (CorInfoCallConvExtension)value.A;
}

void MethodContext::recFindSig(CORINFO_MODULE_HANDLE  moduleHandle,
                               unsigned               sigTOK,
                               CORINFO_CONTEXT_HANDLE context,
                               CORINFO_SIG_INFO*      sig)
{
    if (FindSig == nullptr)
        FindSig = new LightWeightMap<Agnostic_FindSig, Agnostic_CORINFO_SIG_INFO>();

    Agnostic_FindSig key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.module  = CastHandle(moduleHandle);
    key.sigTOK  = (DWORD)sigTOK;
    key.context = CastHandle(context);

    Agnostic_CORINFO_SIG_INFO value = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(*sig, FindSig, SigInstHandleMap);

    FindSig->Add(key, value);
    DEBUG_REC(dmpFindSig(key, value));
}
void MethodContext::dmpFindSig(const Agnostic_FindSig& key, const Agnostic_CORINFO_SIG_INFO& value)
{
    printf("FindSig key module-%016" PRIX64 " sigTOK-%08X context-%016" PRIX64 "", key.module, key.sigTOK, key.context);
    printf(", value-%s",
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value, FindSig, SigInstHandleMap).c_str());
}
void MethodContext::repFindSig(CORINFO_MODULE_HANDLE  moduleHandle,
                               unsigned               sigTOK,
                               CORINFO_CONTEXT_HANDLE context,
                               CORINFO_SIG_INFO*      sig)
{
    Agnostic_FindSig key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.module  = CastHandle(moduleHandle);
    key.sigTOK  = (DWORD)sigTOK;
    key.context = CastHandle(context);

    Agnostic_CORINFO_SIG_INFO value = LookupByKeyOrMissNoMessage(FindSig, key);

    DEBUG_REP(dmpFindSig(key, value));

    *sig = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value, FindSig, SigInstHandleMap);
}

void MethodContext::recGetEEInfo(CORINFO_EE_INFO* pEEInfoOut)
{
    if (GetEEInfo == nullptr)
        GetEEInfo = new LightWeightMap<DWORD, Agnostic_CORINFO_EE_INFO>();

    Agnostic_CORINFO_EE_INFO value;

    value.inlinedCallFrameInfo.size                  = (DWORD)pEEInfoOut->inlinedCallFrameInfo.size;
    value.inlinedCallFrameInfo.offsetOfGSCookie      = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie;
    value.inlinedCallFrameInfo.offsetOfFrameVptr     = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr;
    value.inlinedCallFrameInfo.offsetOfFrameLink     = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink;
    value.inlinedCallFrameInfo.offsetOfCallSiteSP    = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP;
    value.inlinedCallFrameInfo.offsetOfCalleeSavedFP = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP;
    value.inlinedCallFrameInfo.offsetOfCallTarget    = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget;
    value.inlinedCallFrameInfo.offsetOfReturnAddress = (DWORD)pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress;
    value.offsetOfThreadFrame                        = (DWORD)pEEInfoOut->offsetOfThreadFrame;
    value.offsetOfGCState                            = (DWORD)pEEInfoOut->offsetOfGCState;
    value.offsetOfDelegateInstance                   = (DWORD)pEEInfoOut->offsetOfDelegateInstance;
    value.offsetOfDelegateFirstTarget                = (DWORD)pEEInfoOut->offsetOfDelegateFirstTarget;
    value.offsetOfWrapperDelegateIndirectCell        = (DWORD)pEEInfoOut->offsetOfWrapperDelegateIndirectCell;
    value.sizeOfReversePInvokeFrame                  = (DWORD)pEEInfoOut->sizeOfReversePInvokeFrame;
    value.osPageSize                                 = (DWORD)pEEInfoOut->osPageSize;
    value.maxUncheckedOffsetForNullObject            = (DWORD)pEEInfoOut->maxUncheckedOffsetForNullObject;
    value.targetAbi                                  = (DWORD)pEEInfoOut->targetAbi;
    value.osType                                     = (DWORD)pEEInfoOut->osType;

    GetEEInfo->Add(0, value);
    DEBUG_REC(dmpGetEEInfo(0, value));
}
void MethodContext::dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value)
{
    printf("GetEEInfo key %u, value icfi{sz-%u ogs-%u ofv-%u ofl-%u ocsp-%u ocsfp-%u oct-%u ora-%u} "
           "otf-%u ogcs-%u odi-%u odft-%u osdic-%u srpf-%u osps-%u muono-%u tabi-%u osType-%u",
           key, value.inlinedCallFrameInfo.size, value.inlinedCallFrameInfo.offsetOfGSCookie,
           value.inlinedCallFrameInfo.offsetOfFrameVptr, value.inlinedCallFrameInfo.offsetOfFrameLink,
           value.inlinedCallFrameInfo.offsetOfCallSiteSP, value.inlinedCallFrameInfo.offsetOfCalleeSavedFP,
           value.inlinedCallFrameInfo.offsetOfCallTarget, value.inlinedCallFrameInfo.offsetOfReturnAddress,
           value.offsetOfThreadFrame, value.offsetOfGCState, value.offsetOfDelegateInstance,
           value.offsetOfDelegateFirstTarget, value.offsetOfWrapperDelegateIndirectCell,
           value.sizeOfReversePInvokeFrame, value.osPageSize, value.maxUncheckedOffsetForNullObject, value.targetAbi,
           value.osType);
}
void MethodContext::repGetEEInfo(CORINFO_EE_INFO* pEEInfoOut)
{
    Agnostic_CORINFO_EE_INFO value;

    int index = -1;
    if (GetEEInfo != nullptr)
        index = GetEEInfo->GetIndex((DWORD)0);
    if (index >= 0)
    {
        value = GetEEInfo->Get(0);
        DEBUG_REP(dmpGetEEInfo(0, value));

        pEEInfoOut->inlinedCallFrameInfo.size               = (unsigned)value.inlinedCallFrameInfo.size;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie   = (unsigned)value.inlinedCallFrameInfo.offsetOfGSCookie;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr  = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameVptr;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink  = (unsigned)value.inlinedCallFrameInfo.offsetOfFrameLink;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP = (unsigned)value.inlinedCallFrameInfo.offsetOfCallSiteSP;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP =
            (unsigned)value.inlinedCallFrameInfo.offsetOfCalleeSavedFP;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget = (unsigned)value.inlinedCallFrameInfo.offsetOfCallTarget;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress =
            (unsigned)value.inlinedCallFrameInfo.offsetOfReturnAddress;
        pEEInfoOut->offsetOfThreadFrame                = (unsigned)value.offsetOfThreadFrame;
        pEEInfoOut->offsetOfGCState                    = (unsigned)value.offsetOfGCState;
        pEEInfoOut->offsetOfDelegateInstance           = (unsigned)value.offsetOfDelegateInstance;
        pEEInfoOut->offsetOfDelegateFirstTarget        = (unsigned)value.offsetOfDelegateFirstTarget;
        pEEInfoOut->offsetOfWrapperDelegateIndirectCell= (unsigned)value.offsetOfWrapperDelegateIndirectCell;
        pEEInfoOut->sizeOfReversePInvokeFrame          = (unsigned)value.sizeOfReversePInvokeFrame;
        pEEInfoOut->osPageSize                         = (size_t)value.osPageSize;
        pEEInfoOut->maxUncheckedOffsetForNullObject    = (size_t)value.maxUncheckedOffsetForNullObject;
        pEEInfoOut->targetAbi                          = (CORINFO_RUNTIME_ABI)value.targetAbi;
        pEEInfoOut->osType                             = (CORINFO_OS)value.osType;
    }
    else
    {
        pEEInfoOut->inlinedCallFrameInfo.size                  = (unsigned)0x40;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfGSCookie      = (unsigned)0;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameVptr     = (unsigned)0x8;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfFrameLink     = (unsigned)0x10;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCallSiteSP    = (unsigned)0x28;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCalleeSavedFP = (unsigned)0x38;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfCallTarget    = (unsigned)0x18;
        pEEInfoOut->inlinedCallFrameInfo.offsetOfReturnAddress = (unsigned)0x30;
        pEEInfoOut->offsetOfThreadFrame                        = (unsigned)0x10;
        pEEInfoOut->offsetOfGCState                            = (unsigned)0xc;
        pEEInfoOut->offsetOfDelegateInstance                   = (unsigned)0x8;
        pEEInfoOut->offsetOfDelegateFirstTarget                = (unsigned)0x18;
        pEEInfoOut->offsetOfWrapperDelegateIndirectCell        = (unsigned)0x40;
        pEEInfoOut->sizeOfReversePInvokeFrame                  = (unsigned)0x8;
        pEEInfoOut->osPageSize                                 = (size_t)0x1000;
        pEEInfoOut->maxUncheckedOffsetForNullObject            = (size_t)((32 * 1024) - 1);
        pEEInfoOut->targetAbi                                  = CORINFO_CORECLR_ABI;
#ifdef TARGET_OSX
        pEEInfoOut->osType                                     = CORINFO_APPLE;
#elif defined(TARGET_UNIX)
        pEEInfoOut->osType                                     = CORINFO_UNIX;
#else
        pEEInfoOut->osType                                     = CORINFO_WINNT;
#endif
    }
}

void MethodContext::recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal)
{
    if (GetGSCookie == nullptr)
        GetGSCookie = new LightWeightMap<DWORD, DLDL>();

    DLDL value;

    if (pCookieVal != nullptr)
        value.A = (DWORDLONG)*pCookieVal;
    else
        value.A = 0;

    if (ppCookieVal != nullptr)
        value.B = CastPointer(*ppCookieVal);
    else
        value.B = 0;

    GetGSCookie->Add(0, value);
    DEBUG_REC(dmpGetGSCookie(0, value));
}
void MethodContext::dmpGetGSCookie(DWORD key, DLDL value)
{
    printf("GetGSCookie key 0, value pCookieVal-%016" PRIX64 " ppCookieVal-%016" PRIX64 "", value.A, value.B);
}
void MethodContext::repGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal)
{
    if (GetGSCookie == nullptr)
    {
        // fake the result because for the codegen it is just a constant.
        if (pCookieVal != nullptr)
        {
            *pCookieVal = (GSCookie)0x06000000;
        }
        if (ppCookieVal != nullptr)
        {
            *ppCookieVal = (GSCookie*)0x06000001;
        }
        return;
    }

    DLDL value = LookupByKeyOrMissNoMessage(GetGSCookie, 0);

    DEBUG_REP(dmpGetGSCookie(0, value));

    if (pCookieVal != nullptr)
        *pCookieVal = (GSCookie)value.A;
    if (ppCookieVal != nullptr)
        *ppCookieVal = (GSCookie*)value.B;
}

void MethodContext::recGetOSRInfo(PatchpointInfo* patchpointInfo, unsigned* ilOffset)
{
    if (GetOSRInfo == nullptr)
    {
        GetOSRInfo = new LightWeightMap<DWORD, Agnostic_GetOSRInfo>();
    }

    Agnostic_GetOSRInfo value;

    value.index = (DWORD)GetOSRInfo->AddBuffer((const unsigned char*) patchpointInfo, patchpointInfo->PatchpointInfoSize());
    value.ilOffset = *ilOffset;

    // use 0 for key
    DWORD key = 0;
    GetOSRInfo->Add(key, value);
    DEBUG_REC(dmpGetOSRInfo(key, value));
}

void MethodContext::dmpGetOSRInfo(DWORD key, const Agnostic_GetOSRInfo& value)
{
    // todo - dump patchpoint info?
    printf("GetOSRInfo key %u, value patchpointInfo-%u {...} iloffset-%u\n",
        key, value.index, value.ilOffset);
}

PatchpointInfo* MethodContext::repGetOSRInfo(unsigned* ilOffset)
{
    DWORD key = 0;

    Agnostic_GetOSRInfo value = LookupByKeyOrMissNoMessage(GetOSRInfo, key);

    DEBUG_REP(dmpGetOSRInfo(key, value));

    *ilOffset = value.ilOffset;
    return (PatchpointInfo*)GetOSRInfo->GetBuffer(value.index);
}

void MethodContext::recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE   cls,
                                                  CORINFO_MODULE_HANDLE* pModule,
                                                  void**                 ppIndirection,
                                                  size_t                 result)
{
    if (GetClassModuleIdForStatics == nullptr)
        GetClassModuleIdForStatics = new LightWeightMap<DWORDLONG, Agnostic_GetClassModuleIdForStatics>();

    Agnostic_GetClassModuleIdForStatics value;

    if (pModule != nullptr)
        value.Module = CastHandle(*pModule);
    else
        value.Module = 0;
    if (ppIndirection != nullptr)
        value.pIndirection = CastPointer(*ppIndirection);
    else
        value.pIndirection = 0;
    value.result           = (DWORDLONG)result;

    DWORDLONG key = CastHandle(cls);
    GetClassModuleIdForStatics->Add(key, value);
    DEBUG_REC(dmpGetClassModuleIdForStatics(key, value));
}
void MethodContext::dmpGetClassModuleIdForStatics(DWORDLONG key, const Agnostic_GetClassModuleIdForStatics& value)
{
    printf("GetClassModuleIdForStatics key cls-%016" PRIX64 ", value mod-%016" PRIX64 " pp-%016" PRIX64 " res-%016" PRIX64 "", key, value.Module,
           value.pIndirection, value.result);
}
size_t MethodContext::repGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE   cls,
                                                    CORINFO_MODULE_HANDLE* pModule,
                                                    void**                 ppIndirection)
{
    DWORDLONG key = CastHandle(cls);
    Agnostic_GetClassModuleIdForStatics value = LookupByKeyOrMiss(GetClassModuleIdForStatics, key, ": key %016" PRIX64 "", key);

	DEBUG_REP(dmpGetClassModuleIdForStatics(key, value));

    if (pModule != nullptr)
        *pModule = (CORINFO_MODULE_HANDLE)value.Module;
    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.pIndirection;
    return (size_t)value.result;
}

void MethodContext::recGetIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset, bool result)
{
    if (GetIsClassInitedFlagAddress == nullptr)
        GetIsClassInitedFlagAddress = new LightWeightMap<DWORDLONG, Agnostic_GetIsClassInitedFlagAddress>();

    Agnostic_GetIsClassInitedFlagAddress value;
    value.addr.handle = CastHandle(addr->addr);
    value.addr.accessType = (DWORD)addr->accessType;
    value.offset = (DWORD)*offset;
    value.result = (DWORD)result;

    DWORDLONG key = CastHandle(cls);
    GetIsClassInitedFlagAddress->Add(key, value);
    DEBUG_REC(dmpGetIsClassInitedFlagAddress(key, value));
}
void MethodContext::dmpGetIsClassInitedFlagAddress(DWORDLONG key, const Agnostic_GetIsClassInitedFlagAddress& value)
{
    printf("GetIsClassInitedFlagAddress key hnd-%016" PRIX64 ", value addr-%016" PRIX64 ", result-%u", key, value.addr.handle, value.result);
}
bool MethodContext::repGetIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset)
{
    DWORDLONG key = CastHandle(cls);
    Agnostic_GetIsClassInitedFlagAddress value = LookupByKeyOrMiss(GetIsClassInitedFlagAddress, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetIsClassInitedFlagAddress(key, value));

    *offset = (int)value.offset;
    addr->accessType = (InfoAccessType)value.addr.accessType;
    addr->addr = (void*)value.addr.handle;
    return (bool)value.result;
}

void MethodContext::recGetStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr, bool result)
{
    if (GetStaticBaseAddress == nullptr)
        GetStaticBaseAddress = new LightWeightMap<DLD, Agnostic_GetStaticBaseAddress>();

    Agnostic_GetStaticBaseAddress value;
    value.addr.handle = CastHandle(addr->addr);
    value.addr.accessType = (DWORD)addr->accessType;
    value.result = (DWORDLONG)result;

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls);
    key.B = (DWORD)isGc;

    GetStaticBaseAddress->Add(key, value);
    DEBUG_REC(dmpGetStaticBaseAddress(key, value));
}
void MethodContext::dmpGetStaticBaseAddress(DLD key, const Agnostic_GetStaticBaseAddress& value)
{
    printf("GetStaticBaseAddress key hnd-%016" PRIX64 ", value addr-%016" PRIX64 ", result-%u", key.A, value.addr.handle, value.result);
}
bool MethodContext::repGetStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls);
    key.B = (DWORD)isGc;

    Agnostic_GetStaticBaseAddress value = LookupByKeyOrMiss(GetStaticBaseAddress, key, ": key %016" PRIX64 "", key.A);
    DEBUG_REP(dmpGetStaticBaseAddress(key, value));

    addr->accessType = (InfoAccessType)value.addr.accessType;
    addr->addr = (void*)value.addr.handle;
    return (bool)value.result;
}

void MethodContext::recGetThreadTLSIndex(void** ppIndirection, DWORD result)
{
    if (GetThreadTLSIndex == nullptr)
        GetThreadTLSIndex = new LightWeightMap<DWORD, DLD>();

    DLD value;

    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = (DWORD)result;

    GetThreadTLSIndex->Add(0, value);
}
void MethodContext::dmpGetThreadTLSIndex(DWORD key, DLD value)
{
    printf("GetThreadTLSIndex key 0, value ppIndirection-%016" PRIX64 " result-%08X", value.A, value.B);
}
DWORD MethodContext::repGetThreadTLSIndex(void** ppIndirection)
{
    DLD value = LookupByKeyOrMissNoMessage(GetThreadTLSIndex, 0);

	DEBUG_REP(dmpGetThreadTLSIndex(0, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (DWORD)value.B;
}

void MethodContext::recGetAddrOfCaptureThreadGlobal(void** ppIndirection, int32_t* result)
{
    if (GetAddrOfCaptureThreadGlobal == nullptr)
        GetAddrOfCaptureThreadGlobal = new LightWeightMap<DWORD, DLDL>();

    DLDL value;

    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastPointer(result);

    GetAddrOfCaptureThreadGlobal->Add(0, value);
    DEBUG_REC(dmpGetAddrOfCaptureThreadGlobal(0, value));
}
void MethodContext::dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value)
{
    printf("GetAddrOfCaptureThreadGlobal key %u, value ppi-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
int32_t* MethodContext::repGetAddrOfCaptureThreadGlobal(void** ppIndirection)
{
    if ((GetAddrOfCaptureThreadGlobal == nullptr) || (GetAddrOfCaptureThreadGlobal->GetIndex((DWORD)0) == -1))
    {
#ifdef sparseMC
        LogDebug("Sparse - repGetAddrOfCaptureThreadGlobal returning nullptr and 0xCAFE0001");
        if (ppIndirection != nullptr)
            *ppIndirection = nullptr;
        return (int32_t*)(size_t)0xCAFE0001;
#else
        LogException(EXCEPTIONCODE_MC, "Didn't find anything for GetAddrOfCaptureThreadGlobal", "");
#endif
    }

    DLDL value = GetAddrOfCaptureThreadGlobal->Get(0);
    DEBUG_REP(dmpGetAddrOfCaptureThreadGlobal(0, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (int32_t*)value.B;
}

void MethodContext::recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection, unsigned result)
{
    if (GetClassDomainID == nullptr)
        GetClassDomainID = new LightWeightMap<DWORDLONG, DLD>();

    DLD value;

    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = (DWORD)result;

    DWORDLONG key = CastHandle(cls);
    GetClassDomainID->Add(key, value);
    DEBUG_REC(dmpGetClassDomainID(key, value));
}
void MethodContext::dmpGetClassDomainID(DWORDLONG key, DLD value)
{
    printf("GetClassDomainID key cls-%016" PRIX64 ", value pp-%016" PRIX64 " res-%u", key, value.A, value.B);
}
unsigned MethodContext::repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection)
{
    DWORDLONG key = CastHandle(cls);
    DLD value = LookupByKeyOrMiss(GetClassDomainID, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetClassDomainID(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (unsigned)value.B;
}

void MethodContext::recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* result)
{
    if (GetLocationOfThisType == nullptr)
        GetLocationOfThisType = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND>();

    Agnostic_CORINFO_LOOKUP_KIND value = SpmiRecordsHelper::CreateAgnostic_CORINFO_LOOKUP_KIND(result);

    DWORDLONG key = CastHandle(context);
    GetLocationOfThisType->Add(key, value);
    DEBUG_REC(dmpGetLocationOfThisType(key, value));
}
void MethodContext::dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value)
{
    printf("GetLocationOfThisType key ftn-%016" PRIX64 ", value %s", key,
           SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP_KIND(value).c_str());
}
void MethodContext::repGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind)
{
    DWORDLONG key = CastHandle(context);
    Agnostic_CORINFO_LOOKUP_KIND value = LookupByKeyOrMiss(GetLocationOfThisType, key, ": key %016" PRIX64 "", key);
	DEBUG_REP(dmpGetLocationOfThisType(key, value));
    *pLookupKind = SpmiRecordsHelper::RestoreCORINFO_LOOKUP_KIND(value);
}

void MethodContext::recGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd,
                                       CORINFO_CLASS_HANDLE  clsHnd,
                                       CORINFO_METHOD_HANDLE targetMethodHnd,
                                       DelegateCtorArgs*     pCtorData,
                                       CORINFO_METHOD_HANDLE result)
{
    if (GetDelegateCtor == nullptr)
        GetDelegateCtor = new LightWeightMap<Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut>();

    Agnostic_GetDelegateCtorIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.methHnd         = CastHandle(methHnd);
    key.clsHnd          = CastHandle(clsHnd);
    key.targetMethodHnd = CastHandle(targetMethodHnd);

    Agnostic_GetDelegateCtorOut value;
    value.CtorData.pMethod = CastPointer(pCtorData->pMethod);
    value.CtorData.pArg3   = CastPointer(pCtorData->pArg3);
    value.CtorData.pArg4   = CastPointer(pCtorData->pArg4);
    value.CtorData.pArg5   = CastPointer(pCtorData->pArg5);
    value.result           = CastHandle(result);

    GetDelegateCtor->Add(key, value);
    DEBUG_REC(dmpGetDelegateCtor(key, value));
}
void MethodContext::dmpGetDelegateCtor(const Agnostic_GetDelegateCtorIn& key, const Agnostic_GetDelegateCtorOut& value)
{
    printf("GetDelegateCtor key ftn-%016" PRIX64 " cls-%016" PRIX64 " tftn-%016" PRIX64 ", value pm-%016" PRIX64 " a3-%016" PRIX64 " a4-%016" PRIX64 " "
           "a5-%016" PRIX64 " res-%016" PRIX64 "",
           key.methHnd, key.clsHnd, key.targetMethodHnd, value.CtorData.pMethod, value.CtorData.pArg3,
           value.CtorData.pArg4, value.CtorData.pArg5, value.result);
}
CORINFO_METHOD_HANDLE MethodContext::repGetDelegateCtor(CORINFO_METHOD_HANDLE methHnd,
                                                        CORINFO_CLASS_HANDLE  clsHnd,
                                                        CORINFO_METHOD_HANDLE targetMethodHnd,
                                                        DelegateCtorArgs*     pCtorData)
{
    Agnostic_GetDelegateCtorIn key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.methHnd         = CastHandle(methHnd);
    key.clsHnd          = CastHandle(clsHnd);
    key.targetMethodHnd = CastHandle(targetMethodHnd);

    Agnostic_GetDelegateCtorOut value = LookupByKeyOrMiss(GetDelegateCtor, key, ": key %016" PRIX64 "", key.methHnd);

    DEBUG_REP(dmpGetDelegateCtor(key, value));

    pCtorData->pMethod = (void*)value.CtorData.pMethod;
    pCtorData->pArg3   = (void*)value.CtorData.pArg3;
    pCtorData->pArg4   = (void*)value.CtorData.pArg4;
    pCtorData->pArg5   = (void*)value.CtorData.pArg5;
    return (CORINFO_METHOD_HANDLE)value.result;
}

void MethodContext::recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult)
{
    if (GetFunctionFixedEntryPoint == nullptr)
        GetFunctionFixedEntryPoint = new LightWeightMap<DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP>();

    Agnostic_CORINFO_CONST_LOOKUP value = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(pResult);

    DWORDLONG key = CastHandle(ftn);
    GetFunctionFixedEntryPoint->Add(key, value);
    DEBUG_REC(dmpGetFunctionFixedEntryPoint(key, value));
}
void MethodContext::dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value)
{
    printf("GetFunctionFixedEntryPoint key ftn-%016" PRIX64 ", value %s", key,
           SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value).c_str());
}
void MethodContext::repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult)
{
    // The isUnsafeFunctionPointer has no impact on the resulting value.
    // It helps produce side-effects in the runtime only.
    DWORDLONG key = CastHandle(ftn);
    Agnostic_CORINFO_CONST_LOOKUP value = LookupByKeyOrMiss(GetFunctionFixedEntryPoint, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetFunctionFixedEntryPoint(key, value));
    *pResult = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value);
}

void MethodContext::recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result)
{
    if (GetFieldInClass == nullptr)
        GetFieldInClass = new LightWeightMap<DLD, DWORDLONG>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(clsHnd);
    key.B = (DWORD)num;

    DWORDLONG value = CastHandle(result);
    GetFieldInClass->Add(key, value);
    DEBUG_REC(dmpGetFieldInClass(key, value));
}
void MethodContext::dmpGetFieldInClass(DLD key, DWORDLONG value)
{
    printf("GetFieldInClass key cls-%016" PRIX64 " ind-%u, value %016" PRIX64 "", key.A, key.B, value);
}
CORINFO_FIELD_HANDLE MethodContext::repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(clsHnd);
    key.B = (DWORD)num;

    DWORDLONG value = LookupByKeyOrMiss(GetFieldInClass, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetFieldInClass(key, value));
    CORINFO_FIELD_HANDLE result = (CORINFO_FIELD_HANDLE)value;
    return result;
}

void MethodContext::recGetTypeLayout(GetTypeLayoutResult result, CORINFO_CLASS_HANDLE typeHnd, CORINFO_TYPE_LAYOUT_NODE* nodes, size_t numNodes)
{
    if (GetTypeLayout == nullptr)
        GetTypeLayout = new LightWeightMap<DWORDLONG, Agnostic_GetTypeLayoutResult>();

    DWORDLONG key = CastHandle(typeHnd);

    int index = GetTypeLayout->GetIndex(key);
    if (index != -1)
    {
        // If we already have this then just skip.
        Agnostic_GetTypeLayoutResult existing = GetTypeLayout->GetItem(index);
        if ((existing.result != (DWORD)GetTypeLayoutResult::Partial) ||
            (result == GetTypeLayoutResult::Failure) ||
            (numNodes <= existing.numNodes))
        {
            // No new data to add
            return;
        }

        // Otherwise fall through and update the map with more information.
    }

    Agnostic_GetTypeLayoutResult value;
    ZeroMemory(&value, sizeof(value));

    value.result = (DWORD)result;
    if (result == GetTypeLayoutResult::Failure)
    {
        value.nodesBuffer = UINT_MAX;
    }
    else
    {
        Agnostic_CORINFO_TYPE_LAYOUT_NODE* agnosticFields = new Agnostic_CORINFO_TYPE_LAYOUT_NODE[numNodes];
        for (size_t i = 0; i < numNodes; i++)
        {
            agnosticFields[i] = SpmiRecordsHelper::StoreAgnostic_CORINFO_TYPE_LAYOUT_NODE(nodes[i]);
        }

        value.nodesBuffer = GetTypeLayout->AddBuffer((unsigned char*)agnosticFields, (unsigned int)(sizeof(Agnostic_CORINFO_TYPE_LAYOUT_NODE) * numNodes));
        value.numNodes = (DWORD)numNodes;

        delete[] agnosticFields;
    }

    if (index != -1)
    {
        GetTypeLayout->Update(index, value);
    }
    else
    {
        GetTypeLayout->Add(key, value);
    }
}
void MethodContext::dmpGetTypeLayout(DWORDLONG key, const Agnostic_GetTypeLayoutResult& value)
{
    printf("GetTypeLayout key type-%016" PRIX64 " value result=%d numNodes=%d", key, (DWORD)value.result, value.numNodes);
    if (value.numNodes > 0)
    {
        Agnostic_CORINFO_TYPE_LAYOUT_NODE* nodes = reinterpret_cast<Agnostic_CORINFO_TYPE_LAYOUT_NODE*>(GetTypeLayout->GetBuffer(value.nodesBuffer));
        size_t index = 0;
        dmpTypeLayoutTree(nodes, value.numNodes, &index, 1);
    }
}
void MethodContext::dmpTypeLayoutTree(const Agnostic_CORINFO_TYPE_LAYOUT_NODE* nodes, size_t maxNodes, size_t* index, size_t indent)
{
    for (size_t i = 0; i < indent; i++)
    {
        printf("  ");
    }

    const Agnostic_CORINFO_TYPE_LAYOUT_NODE* node = &nodes[*index];
    printf("%zu: parent %u offset %u size %u type %s numFields %u hasSignificantPadding %s simdTypeHnd %016" PRIX64 " diagFieldHnd %016" PRIX64,
        *index,
        node->parent,
        node->offset,
        node->size,
        toString((CorInfoType)node->type),
        node->numFields,
        node->hasSignificantPadding ? "yes" : "no",
        node->simdTypeHnd,
        node->diagFieldHnd);

    (*index)++;
    for (size_t i = 0; i < node->numFields; i++)
    {
        if (i >= maxNodes)
            break;

        dmpTypeLayoutTree(nodes, maxNodes, index, indent + 1);
    }
}
GetTypeLayoutResult MethodContext::repGetTypeLayout(CORINFO_CLASS_HANDLE typeHnd, CORINFO_TYPE_LAYOUT_NODE* nodes, size_t* numNodes)
{
    DWORDLONG key = CastHandle(typeHnd);
    Agnostic_GetTypeLayoutResult value = LookupByKeyOrMiss(GetTypeLayout, key, ": key type-%016" PRIX64, key);

    GetTypeLayoutResult result = (GetTypeLayoutResult)value.result;
    if (result == GetTypeLayoutResult::Failure)
    {
        return result;
    }

    Agnostic_CORINFO_TYPE_LAYOUT_NODE* valueFields = (Agnostic_CORINFO_TYPE_LAYOUT_NODE*)GetTypeLayout->GetBuffer(value.nodesBuffer);
    size_t nodesToWrite = min((size_t)value.numNodes, *numNodes);
    for (size_t i = 0; i < nodesToWrite; i++)
    {
        nodes[i] = SpmiRecordsHelper::RestoreCORINFO_TYPE_LAYOUT_NODE(valueFields[i]);
    }

    *numNodes = nodesToWrite;
    return (result == GetTypeLayoutResult::Partial) || (value.numNodes > *numNodes) ? GetTypeLayoutResult::Partial : GetTypeLayoutResult::Success;
}

void MethodContext::recGetFieldType(CORINFO_FIELD_HANDLE  field,
                                    CORINFO_CLASS_HANDLE* structType,
                                    CORINFO_CLASS_HANDLE  memberParent,
                                    CorInfoType           result)
{
    if (GetFieldType == nullptr)
        GetFieldType = new LightWeightMap<DLDL, DLD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(field);
    key.B = CastHandle(memberParent);

    DLD value;
    value.B = (DWORD)result;
    if (structType == nullptr)
    {
        value.A = 0;
    }
    else
    {
        value.A = CastHandle(*structType);

        // If we had a previous call with null 'structType', we will not have captured the
        // class handle (we use only 'field' and 'memberParent' as keys).
        // Update the value in that case.
        unsigned index = GetFieldType->GetIndex(key);
        if ((index != (unsigned)-1) && (GetFieldType->GetItem(index).A == 0))
        {
            GetFieldType->Update(index, value);
            DEBUG_REC(dmpGetFieldType(key, value));
            return;
        }
    }
    GetFieldType->Add(key, value);
    DEBUG_REC(dmpGetFieldType(key, value));
}
void MethodContext::dmpGetFieldType(DLDL key, DLD value)
{
    printf("GetFieldType key fld-%016" PRIX64 " cls-%016" PRIX64 ", value ch-%016" PRIX64 " cit-%u(%s)", key.A, key.B, value.A, value.B,
           toString((CorInfoType)value.B));
}
CorInfoType MethodContext::repGetFieldType(CORINFO_FIELD_HANDLE  field,
                                           CORINFO_CLASS_HANDLE* structType,
                                           CORINFO_CLASS_HANDLE  memberParent)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(field);
    key.B = CastHandle(memberParent);

    DLD value = LookupByKeyOrMiss(GetFieldType, key, ": key %016" PRIX64 "", key.A);

    DEBUG_REP(dmpGetFieldType(key, value));

    if (structType != nullptr)
        *structType = (CORINFO_CLASS_HANDLE)value.A;
    return (CorInfoType)value.B;
}

void MethodContext::recSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE  parent,
                                                  CORINFO_METHOD_HANDLE method,
                                                  bool                  result)
{
    if (SatisfiesMethodConstraints == nullptr)
        SatisfiesMethodConstraints = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(parent);
    key.B = CastHandle(method);

    DWORD value = result ? 1 : 0;
    SatisfiesMethodConstraints->Add(key, value);
    DEBUG_REC(dmpSatisfiesMethodConstraints(key, value));
}
void MethodContext::dmpSatisfiesMethodConstraints(DLDL key, DWORD value)
{
    printf("SatisfiesMethodConstraints key cls-%016" PRIX64 " ftn-%016" PRIX64 ", value res-%u", key.A, key.B, value);
}
bool MethodContext::repSatisfiesMethodConstraints(CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(parent);
    key.B = CastHandle(method);

    DWORD value = LookupByKeyOrMissNoMessage(SatisfiesMethodConstraints, key);

    DEBUG_REP(dmpSatisfiesMethodConstraints(key, value));
    return value != 0;
}

void MethodContext::recGetStringLiteral(CORINFO_MODULE_HANDLE module, unsigned metaTOK, char16_t* buffer, int bufferSize, int startIndex, int length)
{
    if (GetStringLiteral == nullptr)
        GetStringLiteral = new LightWeightMap<DLDDD, DD>();

    DLDDD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(module);
    key.B = (DWORD)metaTOK;
    key.C = (DWORD)bufferSize;
    key.D = (DWORD)startIndex;

    DWORD strBuf = (DWORD)-1;
    if (buffer != nullptr && length != -1)
    {
        int bufferRealSize = min(length, bufferSize) * sizeof(char16_t);
        strBuf = (DWORD)GetStringLiteral->AddBuffer((unsigned char*)buffer, (unsigned int)bufferRealSize);
    }

    DD value;
    value.A = (DWORD)length;
    value.B = (DWORD)strBuf;

    GetStringLiteral->Add(key, value);
    DEBUG_REC(dmpGetStringLiteral(key, value));
}

void MethodContext::dmpGetStringLiteral(DLDDD key, DD value)
{
    printf("GetStringLiteral key mod-%016" PRIX64 " tok-%08X, bufSize-%u, startIndex-%u, len-%u", key.A, key.B, key.C, key.D, value.A);
    GetStringLiteral->Unlock();
}

int MethodContext::repGetStringLiteral(CORINFO_MODULE_HANDLE module, unsigned metaTOK, char16_t* buffer, int bufferSize, int startIndex)
{
    DLDDD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(module);
    key.B = (DWORD)metaTOK;
    key.C = (DWORD)bufferSize;
    key.D = (DWORD)startIndex;

    DD value = LookupByKeyOrMiss(GetStringLiteral, key, ": key handle-%016" PRIX64 " token-%X bufferSize-%d startIndex-%d", key.A, key.B, key.C, key.D);

    DEBUG_REP(dmpGetStringLiteral(key, value));
    int srcBufferLength = (int)value.A;
    if (buffer != nullptr && srcBufferLength > 0)
    {
        char16_t* srcBuffer = (char16_t*)GetStringLiteral->GetBuffer(value.B);
        Assert(srcBuffer != nullptr);
        memcpy(buffer, srcBuffer, min(srcBufferLength, bufferSize) * sizeof(char16_t));
    }
    return srcBufferLength;
}

void MethodContext::recCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent, bool result)
{
    if (CanCast == nullptr)
        CanCast = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(child);
    key.B = CastHandle(parent);

    DWORD value = result ? 1 : 0;
    CanCast->Add(key, value);
    DEBUG_REC(dmpCanCast(key, value));
}
void MethodContext::dmpCanCast(DLDL key, DWORD value)
{
    printf("CanCast key chd-%016" PRIX64 " par-%016" PRIX64 ", value res-%u", key.A, key.B, value);
}
bool MethodContext::repCanCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(child);
    key.B = CastHandle(parent);

    DWORD value = LookupByKeyOrMiss(CanCast, key, ": key %016" PRIX64 " %016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpCanCast(key, value));
    return value != 0;
}

void MethodContext::recGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE* clsRet, CorInfoType result)
{
    if (GetChildType == nullptr)
        GetChildType = new LightWeightMap<DWORDLONG, DLD>();

    DLD value;
    value.A = CastHandle(*clsRet);
    value.B = (DWORD)result;

    DWORDLONG key = CastHandle(clsHnd);
    GetChildType->Add(key, value);
    DEBUG_REC(dmpGetChildType(key, value));
}
void MethodContext::dmpGetChildType(DWORDLONG key, DLD value)
{
    printf("GetChildType key cls-%016" PRIX64 ", value clsr-%016" PRIX64 " cit-%u(%s)", key, value.A, value.B,
           toString((CorInfoType)value.B));
}
CorInfoType MethodContext::repGetChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE* clsRet)
{
    DWORDLONG key = CastHandle(clsHnd);
    DLD value = LookupByKeyOrMiss(GetChildType, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetChildType(key, value));

    *clsRet = (CORINFO_CLASS_HANDLE)value.A;
    return (CorInfoType)value.B;
}

void MethodContext::recGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size, void* result)
{
    if (GetArrayInitializationData == nullptr)
        GetArrayInitializationData = new LightWeightMap<DLD, DWORDLONG>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(field);
    key.B = (DWORD)size;

    DWORDLONG value = CastPointer(result);
    GetArrayInitializationData->Add(key, value);
    DEBUG_REC(dmpGetArrayInitializationData(key, value));
}
void MethodContext::dmpGetArrayInitializationData(DLD key, DWORDLONG value)
{
    printf("GetArrayInitializationData key field-%016" PRIX64 " size-%08X, value result-%016" PRIX64 "", key.A, key.B, value);
}
void* MethodContext::repGetArrayInitializationData(CORINFO_FIELD_HANDLE field, DWORD size)
{
    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(field);
    key.B = (DWORD)size;

    DWORDLONG value = LookupByKeyOrMissNoMessage(GetArrayInitializationData, key);

    DEBUG_REP(dmpGetArrayInitializationData(key, value));
    void* result = (void*)value;
    return result;
}

void MethodContext::recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup)
{
    if (GetAddressOfPInvokeTarget == nullptr)
        GetAddressOfPInvokeTarget = new LightWeightMap<DWORDLONG, DLD>();

    DLD value;
    value.A = CastPointer(pLookup->addr);
    value.B = (DWORD)pLookup->accessType;

    DWORDLONG key = CastHandle(method);
    GetAddressOfPInvokeTarget->Add(key, value);
    DEBUG_REC(dmpGetAddressOfPInvokeTarget(key, value));
}
void MethodContext::dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value)
{
    printf("GetAddressOfPInvokeTarget key ftn-%016" PRIX64 ", value addr-%016" PRIX64 " at-%u", key, value.A, value.B);
}
void MethodContext::repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup)
{
    DWORDLONG key = CastHandle(method);
    DLD value = LookupByKeyOrMiss(GetAddressOfPInvokeTarget, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetAddressOfPInvokeTarget(key, value));

    pLookup->addr       = (void*)value.A;
    pLookup->accessType = (InfoAccessType)value.B;
}

void MethodContext::recGetMethodHash(CORINFO_METHOD_HANDLE ftn, unsigned result)
{
    if (GetMethodHash == nullptr)
        GetMethodHash = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(ftn);
    DWORD value = (DWORD)result;
    GetMethodHash->Add(key, value);
    DEBUG_REC(dmpGetMethodHash(key, value));
}
void MethodContext::dmpGetMethodHash(DWORDLONG key, DWORD value)
{
    printf("GetMethodHash key %016" PRIX64 ", value %u", key, value);
}
unsigned MethodContext::repGetMethodHash(CORINFO_METHOD_HANDLE ftn)
{
    DWORDLONG key = CastHandle(ftn);
    unsigned result = 0x43;
    if (GetMethodHash != nullptr)
        if (GetMethodHash->GetIndex(key) >= 0)
            result = GetMethodHash->Get(key);
    DEBUG_REP(dmpGetMethodHash(key, (DWORD)result));
    return result;
}

void MethodContext::recCanTailCall(CORINFO_METHOD_HANDLE callerHnd,
                                   CORINFO_METHOD_HANDLE declaredCalleeHnd,
                                   CORINFO_METHOD_HANDLE exactCalleeHnd,
                                   bool                  fIsTailPrefix,
                                   bool                  result)
{
    if (CanTailCall == nullptr)
        CanTailCall = new LightWeightMap<Agnostic_CanTailCall, DWORD>();

    Agnostic_CanTailCall key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.callerHnd         = CastHandle(callerHnd);
    key.declaredCalleeHnd = CastHandle(declaredCalleeHnd);
    key.exactCalleeHnd    = CastHandle(exactCalleeHnd);
    key.fIsTailPrefix     = (DWORD)fIsTailPrefix;

    DWORD value = result ? 1 : 0;
    CanTailCall->Add(key, value);
    DEBUG_REC(dmpCanTailCall(key, value));
}
void MethodContext::dmpCanTailCall(const Agnostic_CanTailCall& key, DWORD value)
{
    printf("CanTailCall key clr-%016" PRIX64 " dcle-%016" PRIX64 " ecle-%016" PRIX64 " pfx-%u, value res-%u", key.callerHnd,
           key.declaredCalleeHnd, key.exactCalleeHnd, key.fIsTailPrefix, value);
}
bool MethodContext::repCanTailCall(CORINFO_METHOD_HANDLE callerHnd,
                                   CORINFO_METHOD_HANDLE declaredCalleeHnd,
                                   CORINFO_METHOD_HANDLE exactCalleeHnd,
                                   bool                  fIsTailPrefix)
{
    Agnostic_CanTailCall key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.callerHnd         = CastHandle(callerHnd);
    key.declaredCalleeHnd = CastHandle(declaredCalleeHnd);
    key.exactCalleeHnd    = CastHandle(exactCalleeHnd);
    key.fIsTailPrefix     = (DWORD)fIsTailPrefix;

    DWORD value = LookupByKeyOrMiss(CanTailCall, key, ": key %016" PRIX64 "", key.callerHnd);

    DEBUG_REP(dmpCanTailCall(key, value));
    return value != 0;
}

void MethodContext::recIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE  delegateHnd,
                                                 CORINFO_METHOD_HANDLE calleeHnd,
                                                 bool                  result)
{
    if (IsDelegateCreationAllowed == nullptr)
        IsDelegateCreationAllowed = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(delegateHnd);
    key.B = CastHandle(calleeHnd);

    DWORD value = result ? 1 : 0;
    IsDelegateCreationAllowed->Add(key, value);
    DEBUG_REC(dmpIsDelegateCreationAllowed(key, value));
}
void MethodContext::dmpIsDelegateCreationAllowed(DLDL key, DWORD value)
{
    printf("IsDelegateCreationAllowed key delegateHnd-%016" PRIX64 " calleeHnd-%016" PRIX64 " result-%08X", key.A, key.B, value);
}
bool MethodContext::repIsDelegateCreationAllowed(CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(delegateHnd);
    key.B = CastHandle(calleeHnd);

    DWORD value = LookupByKeyOrMissNoMessage(IsDelegateCreationAllowed, key);

    DEBUG_REP(dmpIsDelegateCreationAllowed(key, value));
    return value != 0;
}

void MethodContext::recFindCallSiteSig(CORINFO_MODULE_HANDLE  module,
                                       unsigned               methTOK,
                                       CORINFO_CONTEXT_HANDLE context,
                                       CORINFO_SIG_INFO*      sig)
{
    if (FindCallSiteSig == nullptr)
        FindCallSiteSig = new LightWeightMap<Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO>();

    Agnostic_FindCallSiteSig key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.module  = CastHandle(module);
    key.methTok = (DWORD)methTOK;
    key.context = CastHandle(context);

    Agnostic_CORINFO_SIG_INFO value = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(*sig, FindCallSiteSig, SigInstHandleMap);

    FindCallSiteSig->Add(key, value);
    DEBUG_REC(dmpFindCallSiteSig(key, value));
}
void MethodContext::dmpFindCallSiteSig(const Agnostic_FindCallSiteSig& key, const Agnostic_CORINFO_SIG_INFO& value)
{
    printf("dmpFindCallSiteSig key module-%016" PRIX64 " methTok-%08X context-%016" PRIX64 "", key.module, key.methTok, key.context);
    printf(", value-%s",
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(value, FindCallSiteSig, SigInstHandleMap).c_str());
}
void MethodContext::repFindCallSiteSig(CORINFO_MODULE_HANDLE  module,
                                       unsigned               methTOK,
                                       CORINFO_CONTEXT_HANDLE context,
                                       CORINFO_SIG_INFO*      sig)
{
    Agnostic_FindCallSiteSig key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.module  = CastHandle(module);
    key.methTok = (DWORD)methTOK;
    key.context = CastHandle(context);

    Agnostic_CORINFO_SIG_INFO value = LookupByKeyOrMiss(FindCallSiteSig, key, ": key %08X", key.methTok);

    DEBUG_REP(dmpFindCallSiteSig(key, value));

    *sig = SpmiRecordsHelper::Restore_CORINFO_SIG_INFO(value, FindCallSiteSig, SigInstHandleMap);
}

void MethodContext::recGetMethodSync(CORINFO_METHOD_HANDLE ftn, void** ppIndirection, void* result)
{
    if (GetMethodSync == nullptr)
        GetMethodSync = new LightWeightMap<DWORDLONG, DLDL>();
    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastPointer(result);

    DWORDLONG key = CastHandle(ftn);
    GetMethodSync->Add(key, value);
    DEBUG_REC(dmpGetMethodSync(key, value));
}
void MethodContext::dmpGetMethodSync(DWORDLONG key, DLDL value)
{
    printf("GetMethodSync key %016" PRIX64 ", value pp-%016" PRIX64 " res-%016" PRIX64 "", key, value.A, value.B);
}
void* MethodContext::repGetMethodSync(CORINFO_METHOD_HANDLE ftn, void** ppIndirection)
{
    DWORDLONG key = CastHandle(ftn);
    DLDL value = LookupByKeyOrMiss(GetMethodSync, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetMethodSync(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (void*)value.B;
}

void MethodContext::recGetVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection, CORINFO_VARARGS_HANDLE result)
{
    if (GetVarArgsHandle == nullptr)
        GetVarArgsHandle = new LightWeightMap<GetVarArgsHandleValue, DLDL>();

    GetVarArgsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.cbSig      = (DWORD)pSig->cbSig;
    key.pSig_Index = (DWORD)GetVarArgsHandle->AddBuffer((unsigned char*)pSig->pSig, pSig->cbSig);
    key.scope      = CastHandle(pSig->scope);
    key.token      = (DWORD)pSig->token;

    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastHandle(result);

    GetVarArgsHandle->Add(key, value);
    DEBUG_REC(dmpGetVarArgsHandle(key, value));
}
void MethodContext::dmpGetVarArgsHandle(const GetVarArgsHandleValue& key, DLDL value)
{
    printf("GetVarArgsHandle key sig-%s scope-%016" PRIX64 " token-%08X",
        SpmiDumpHelper::DumpPSig(key.pSig_Index, key.cbSig, GetVarArgsHandle).c_str(),
        key.scope, key.token);
    printf(", value ppIndirection-%016" PRIX64 " result-%016" PRIX64 "", value.A, value.B);
}
CORINFO_VARARGS_HANDLE MethodContext::repGetVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection)
{
    GetVarArgsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.cbSig      = (DWORD)pSig->cbSig;
    key.pSig_Index = (DWORD)GetVarArgsHandle->Contains((unsigned char*)pSig->pSig, pSig->cbSig);
    key.scope      = CastHandle(pSig->scope);
    key.token      = (DWORD)pSig->token;

    DLDL value = LookupByKeyOrMissNoMessage(GetVarArgsHandle, key);

    DEBUG_REP(dmpGetVarArgsHandle(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_VARARGS_HANDLE)value.B;
}

void MethodContext::recCanGetVarArgsHandle(CORINFO_SIG_INFO* pSig, bool result)
{
    if (CanGetVarArgsHandle == nullptr)
        CanGetVarArgsHandle = new LightWeightMap<CanGetVarArgsHandleValue, DWORD>();

    CanGetVarArgsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.scope = CastHandle(pSig->scope);
    key.token = (DWORD)pSig->token;

    DWORD value = result ? 1 : 0;
    CanGetVarArgsHandle->Add(key, value);
    DEBUG_REC(dmpCanGetVarArgsHandle(key, value));
}
void MethodContext::dmpCanGetVarArgsHandle(const CanGetVarArgsHandleValue& key, DWORD value)
{
    printf("CanGetVarArgsHandle key scope-%016" PRIX64 " token-%08X, value result-%08X", key.scope, key.token, value);
}
bool MethodContext::repCanGetVarArgsHandle(CORINFO_SIG_INFO* pSig)
{
    CanGetVarArgsHandleValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.scope = CastHandle(pSig->scope);
    key.token = (DWORD)pSig->token;

    DWORD value = LookupByKeyOrMiss(CanGetVarArgsHandle, key, ": key %016" PRIX64 " %08X", key.scope, key.token);

    DEBUG_REP(dmpCanGetVarArgsHandle(key, value));
    return value != 0;
}

void MethodContext::recGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void** ppIndirection, DWORD result)
{
    if (GetFieldThreadLocalStoreID == nullptr)
        GetFieldThreadLocalStoreID = new LightWeightMap<DWORDLONG, DLD>();

    DLD value;

    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = (DWORD)result;

    DWORDLONG key = CastHandle(field);
    GetFieldThreadLocalStoreID->Add(key, value);
    DEBUG_REC(dmpGetFieldThreadLocalStoreID(key, value));
}
void MethodContext::dmpGetFieldThreadLocalStoreID(DWORDLONG key, DLD value)
{
    printf("GetFieldThreadLocalStoreID key field-%016" PRIX64 ", value ppIndirection-%016" PRIX64 " result-%08X", key, value.A,
           value.B);
}
DWORD MethodContext::repGetFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE field, void** ppIndirection)
{
    DWORDLONG key = CastHandle(field);
    DLD value = LookupByKeyOrMiss(GetFieldThreadLocalStoreID, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetFieldThreadLocalStoreID(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (DWORD)value.B;
}

void MethodContext::recAllocPgoInstrumentationBySchema(
    CORINFO_METHOD_HANDLE ftnHnd,
    ICorJitInfo::PgoInstrumentationSchema* pSchema,
    UINT32 countSchemaItems,
    BYTE** pInstrumentationData,
    HRESULT result)
{
    if (AllocPgoInstrumentationBySchema == nullptr)
        AllocPgoInstrumentationBySchema = new LightWeightMap<DWORDLONG, Agnostic_AllocPgoInstrumentationBySchema>();

    Agnostic_AllocPgoInstrumentationBySchema value;

    // PGO schema data poses a couple of challenges from SPMI's perspective:
    //
    // First of, the schema requested from the JIT can reasonably be considered
    // a compile result that might change with different JIT versions and/or
    // environment variables set. So that means SPMI should be able to handle
    // seeing a different schema from recording time.
    //
    // On the other hand, the code generated by the JIT will refer to addresses
    // based on pInstrumentationData and the offsets this function assigns in
    // pSchema. That means this function should be consistent with the recorded
    // EE in how it does layout, to ensure replay is going to match recording
    // time.
    //
    // To handle these issues we store the recorded result of layout for the
    // schema and use that if the JIT's requested schema matches it. Otherwise
    // we use our own layout algorithm for the schema. This layout algorithm is
    // probably not going to match the EE's algorithm exactly, but that's not
    // problematic since a different schema likely means there are going to be
    // diffs anyway.

    value.instrumentationDataAddress = CastPointer(*pInstrumentationData);

    // For the schema, note that we record *after* calling the VM API. Thus, it has already filled in the `Offset` fields.
    // We save the "IN" parts to verify against the replayed call.
    value.countSchemaItems = countSchemaItems;
    Agnostic_PgoInstrumentationSchema* agnosticSchema = (Agnostic_PgoInstrumentationSchema*)malloc(sizeof(Agnostic_PgoInstrumentationSchema) * countSchemaItems);
    for (UINT32 i = 0; i < countSchemaItems; i++)
    {
        agnosticSchema[i].Offset              = (DWORDLONG)pSchema[i].Offset;
        agnosticSchema[i].InstrumentationKind = (DWORD)pSchema[i].InstrumentationKind;
        agnosticSchema[i].ILOffset            = (DWORD)pSchema[i].ILOffset;
        agnosticSchema[i].Count               = (DWORD)pSchema[i].Count;
        agnosticSchema[i].Other               = (DWORD)pSchema[i].Other;
    }
    value.schema_index = AllocPgoInstrumentationBySchema->AddBuffer((unsigned char*)agnosticSchema, sizeof(Agnostic_PgoInstrumentationSchema) * countSchemaItems);
    free(agnosticSchema);
    value.result = (DWORD)result;

    // Even though `countSchemaItems` and (most of) the `pSchema` array are IN parameters, they do not contribute to the lookup key;
    // the `ftnHnd` is the sole key, and the schema passed in for the function is expected to be the same every time the same function
    // handle is used.
    DWORDLONG key = CastHandle(ftnHnd);
    AllocPgoInstrumentationBySchema->Add(key, value);
    DEBUG_REC(dmpAllocPgoInstrumentationBySchema(key, value));
}

void MethodContext::dmpAllocPgoInstrumentationBySchema(DWORDLONG key, const Agnostic_AllocPgoInstrumentationBySchema& value)
{
    printf("AllocPgoInstrumentationBySchema key ftn-%016" PRIX64 ", value res-%08X addr-%016" PRIX64 " cnt-%u schema{\n",
        key, value.result, value.instrumentationDataAddress, value.countSchemaItems);

    if (value.countSchemaItems > 0)
    {
        Agnostic_PgoInstrumentationSchema* pBuf =
            (Agnostic_PgoInstrumentationSchema*)AllocPgoInstrumentationBySchema->GetBuffer(value.schema_index);

        printf("\n");
        for (DWORD i = 0; i < value.countSchemaItems; i++)
        {
            printf(" %u-{Offset %016" PRIX64 " ILOffset %u Kind %u(0x%x) Count %u Other %u}\n",
                i, pBuf[i].Offset, pBuf[i].ILOffset, pBuf[i].InstrumentationKind, pBuf[i].InstrumentationKind, pBuf[i].Count, pBuf[i].Other);
        }
        AllocPgoInstrumentationBySchema->Unlock();
    }
    printf("}");
}


// The following are from <pgo_formatprocessing.h> which we can't include, with
// a few SPMI alterations (related to "target pointers").
static ICorJitInfo::PgoInstrumentationKind operator&(ICorJitInfo::PgoInstrumentationKind a, ICorJitInfo::PgoInstrumentationKind b)
{
    return static_cast<ICorJitInfo::PgoInstrumentationKind>(static_cast<int>(a) & static_cast<int>(b));
}

static unsigned InstrumentationKindToSize(ICorJitInfo::PgoInstrumentationKind kind)
{
    switch(kind & ICorJitInfo::PgoInstrumentationKind::MarshalMask)
    {
        case ICorJitInfo::PgoInstrumentationKind::None:
            return 0;
        case ICorJitInfo::PgoInstrumentationKind::FourByte:
            return 4;
        case ICorJitInfo::PgoInstrumentationKind::EightByte:
            return 8;
        case ICorJitInfo::PgoInstrumentationKind::TypeHandle:
        case ICorJitInfo::PgoInstrumentationKind::MethodHandle:
            return static_cast<unsigned>(SpmiTargetPointerSize());
        default:
            LogError("Unexpected pgo schema data size (kind = %d)", kind);
            return 0;
    }
}

static unsigned InstrumentationKindToAlignment(ICorJitInfo::PgoInstrumentationKind kind)
{
    switch(kind & ICorJitInfo::PgoInstrumentationKind::AlignMask)
    {
        case ICorJitInfo::PgoInstrumentationKind::Align4Byte:
            return 4;
        case ICorJitInfo::PgoInstrumentationKind::Align8Byte:
            return 8;
        case ICorJitInfo::PgoInstrumentationKind::AlignPointer:
            return static_cast<unsigned>(SpmiTargetPointerSize());
        default:
            return InstrumentationKindToSize(kind);
    }
}

// End of pseudo-include

HRESULT MethodContext::repAllocPgoInstrumentationBySchema(
    CORINFO_METHOD_HANDLE ftnHnd,
    ICorJitInfo::PgoInstrumentationSchema* pSchema,
    UINT32 countSchemaItems,
    BYTE** pInstrumentationData)
{
    if (repAllocPgoInstrumentationBySchemaRecorded(ftnHnd, pSchema, countSchemaItems, pInstrumentationData))
    {
        // Matching case: return same layout as recorded EE.
        return S_OK;
    }

    // Do our own layout.
    unsigned maxAlignment = 1;
    unsigned offset = 0;
    for (UINT32 i = 0; i < countSchemaItems; i++)
    {
        unsigned size = InstrumentationKindToSize(pSchema[i].InstrumentationKind) * pSchema[i].Count;
        if (size != 0)
        {
            unsigned alignment = InstrumentationKindToAlignment(pSchema[i].InstrumentationKind);
            maxAlignment = max(maxAlignment, alignment);
            offset = AlignUp(offset, alignment);
            pSchema[i].Offset = offset;
        }

        offset += size;
    }

    // We assume JIT does not write or read from this buffer, only generate
    // code that uses it.
    *pInstrumentationData = (BYTE*)intptr_t(AlignDown(0x12345678u, maxAlignment));
    return S_OK;
}

// Allocate PGO instrumentation by schema in the same way that the original EE did if the schema matches.
// See comment in recAllocPgoInstrumentationBySchema.
// The schema may be partially allocated even if this function returns false.
bool MethodContext::repAllocPgoInstrumentationBySchemaRecorded(
    CORINFO_METHOD_HANDLE ftnHnd,
    ICorJitInfo::PgoInstrumentationSchema* pSchema,
    UINT32 countSchemaItems,
    BYTE** pInstrumentationData)
{
    if (AllocPgoInstrumentationBySchema == nullptr)
        return false;

    DWORDLONG key = CastHandle(ftnHnd);
    int index = AllocPgoInstrumentationBySchema->GetIndex(key);
    if (index == -1)
        return false;

    Agnostic_AllocPgoInstrumentationBySchema value = AllocPgoInstrumentationBySchema->GetItem(index);
    DEBUG_REP(dmpAllocPgoInstrumentationBySchema(key, value));

    if (value.countSchemaItems != countSchemaItems)
        return false;

    Agnostic_PgoInstrumentationSchema* pAgnosticSchema = (Agnostic_PgoInstrumentationSchema*)AllocPgoInstrumentationBySchema->GetBuffer(value.schema_index);
    for (UINT32 iSchema = 0; iSchema < value.countSchemaItems; iSchema++)
    {
        // Everything but `Offset` field is an IN argument, so verify it against what we stored (since we didn't use these
        // IN arguments as part of the key).

        if ((ICorJitInfo::PgoInstrumentationKind)pAgnosticSchema[iSchema].InstrumentationKind != pSchema[iSchema].InstrumentationKind)
            return false;

        if ((int32_t)pAgnosticSchema[iSchema].ILOffset != pSchema[iSchema].ILOffset)
            return false;

        if ((int32_t)pAgnosticSchema[iSchema].Count != pSchema[iSchema].Count)
            return false;

        if ((int32_t)pAgnosticSchema[iSchema].Other != pSchema[iSchema].Other)
            return false;

        pSchema[iSchema].Offset = (size_t)pAgnosticSchema[iSchema].Offset;
    }

    // We assume JIT does not write or read from this buffer, only generate
    // code that uses it.
    *pInstrumentationData = (BYTE*)value.instrumentationDataAddress;
    return true;
}

void MethodContext::recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd,
                                                    ICorJitInfo::PgoInstrumentationSchema** pSchema,
                                                    UINT32* pCountSchemaItems,
                                                    BYTE** pInstrumentationData,
                                                    ICorJitInfo::PgoSource* pPgoSource,
                                                    HRESULT result)
{
    if (GetPgoInstrumentationResults == nullptr)
        GetPgoInstrumentationResults = new LightWeightMap<DWORDLONG, Agnostic_GetPgoInstrumentationResults>();

    Agnostic_GetPgoInstrumentationResults value;

    value.countSchemaItems = *pCountSchemaItems;

    ICorJitInfo::PgoInstrumentationSchema* pInSchema = *pSchema;
    Agnostic_PgoInstrumentationSchema* agnosticSchema = (Agnostic_PgoInstrumentationSchema*)malloc(sizeof(Agnostic_PgoInstrumentationSchema) * (*pCountSchemaItems));
    size_t maxOffset = 0;
    for (UINT32 i = 0; i < (*pCountSchemaItems); i++)
    {
        agnosticSchema[i].Offset              = (DWORDLONG)pInSchema[i].Offset;
        agnosticSchema[i].InstrumentationKind = (DWORD)pInSchema[i].InstrumentationKind;
        agnosticSchema[i].ILOffset            = (DWORD)pInSchema[i].ILOffset;
        agnosticSchema[i].Count               = (DWORD)pInSchema[i].Count;
        agnosticSchema[i].Other               = (DWORD)pInSchema[i].Other;

        unsigned const dataSize = InstrumentationKindToSize(pInSchema[i].InstrumentationKind);
        maxOffset = max(maxOffset, pInSchema[i].Offset + pInSchema[i].Count * dataSize);
    }
    value.schema_index = GetPgoInstrumentationResults->AddBuffer((unsigned char*)agnosticSchema, sizeof(Agnostic_PgoInstrumentationSchema) * (*pCountSchemaItems));
    free(agnosticSchema);

    value.data_index    = GetPgoInstrumentationResults->AddBuffer((unsigned char*)*pInstrumentationData, (unsigned)maxOffset);
    value.dataByteCount = (unsigned)maxOffset;
    value.result        = (DWORD)result;
    value.pgoSource     = (DWORD)*pPgoSource;

    DWORDLONG key = CastHandle(ftnHnd);
    GetPgoInstrumentationResults->Add(key, value);
    DEBUG_REC(dmpGetPgoInstrumentationResults(key, value));
}
void MethodContext::dmpGetPgoInstrumentationResults(DWORDLONG key, const Agnostic_GetPgoInstrumentationResults& value)
{
    printf("GetPgoInstrumentationResults key ftn-%016" PRIX64 ", value res-%08X schemaCnt-%u profileBufSize-%u source-%u schema{",
        key, value.result, value.countSchemaItems, value.dataByteCount, value.pgoSource);

    if (value.countSchemaItems > 0)
    {
        Agnostic_PgoInstrumentationSchema* pBuf =
            (Agnostic_PgoInstrumentationSchema*)GetPgoInstrumentationResults->GetBuffer(value.schema_index);

        BYTE* pInstrumentationData = (BYTE*)GetPgoInstrumentationResults->GetBuffer(value.data_index);

        printf("\n");
        for (DWORD i = 0; i < value.countSchemaItems; i++)
        {
            printf(" %u-{Offset %016" PRIX64 " ILOffset %u Kind %u(0x%x) Count %u Other %u Data ",
                i, pBuf[i].Offset, pBuf[i].ILOffset, pBuf[i].InstrumentationKind, pBuf[i].InstrumentationKind, pBuf[i].Count, pBuf[i].Other);

            switch((ICorJitInfo::PgoInstrumentationKind)pBuf[i].InstrumentationKind)
            {
                case ICorJitInfo::PgoInstrumentationKind::BasicBlockIntCount:
                    printf("B %u", *(unsigned*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::BasicBlockLongCount:
                    printf("B %" PRIu64 "", *(uint64_t*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::EdgeIntCount:
                    printf("E %u", *(unsigned*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::EdgeLongCount:
                    printf("E %" PRIu64 "", *(uint64_t*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount:
                    printf("T %u", *(unsigned*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount:
                    printf("T %" PRIu64 "", *(uint64_t*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes:
                case ICorJitInfo::PgoInstrumentationKind::HandleHistogramMethods:
                    for (unsigned int j = 0; j < pBuf[i].Count; j++)
                    {
                        printf("[%u] %016" PRIX64 " ", j, CastHandle(*(uintptr_t*)(pInstrumentationData + pBuf[i].Offset + j * sizeof(uintptr_t))));
                    }
                    break;
                case ICorJitInfo::PgoInstrumentationKind::ValueHistogramIntCount:
                    printf("V %u", *(unsigned*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::ValueHistogramLongCount:
                    printf("V %" PRIu64 "", *(uint64_t*)(pInstrumentationData + pBuf[i].Offset));
                    break;
                case ICorJitInfo::PgoInstrumentationKind::ValueHistogram:
                    for (unsigned int j = 0; j < pBuf[i].Count; j++)
                    {
                        printf("[%u] %016" PRIX64 " ", j, CastHandle(*(uintptr_t*)(pInstrumentationData + pBuf[i].Offset + j * sizeof(uintptr_t))));
                    }
                    break;
                case ICorJitInfo::PgoInstrumentationKind::GetLikelyClass:
                case ICorJitInfo::PgoInstrumentationKind::GetLikelyMethod:
                    {
                        // (N)umber, (L)ikelihood, (H)andle
                        printf("N %u L %u H %016" PRIX64 "", (unsigned)(pBuf[i].Other >> 8), (unsigned)(pBuf[i].Other && 0xFF), CastHandle(*(uintptr_t*)(pInstrumentationData + pBuf[i].Offset)));
                    }
                    break;
                default:
                    printf("?");
                    break;
            }

            printf("}\n");
        }
        GetPgoInstrumentationResults->Unlock();
    }
    printf("} data_index-%u", value.data_index);
}
HRESULT MethodContext::repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd,
                                                       ICorJitInfo::PgoInstrumentationSchema** pSchema,
                                                       UINT32* pCountSchemaItems,
                                                       BYTE** pInstrumentationData,
                                                       ICorJitInfo::PgoSource* pPgoSource)
{
    DWORDLONG key = CastHandle(ftnHnd);
    Agnostic_GetPgoInstrumentationResults tempValue = LookupByKeyOrMiss(GetPgoInstrumentationResults, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetPgoInstrumentationResults(key, tempValue));

    *pCountSchemaItems    = (UINT32)tempValue.countSchemaItems;
    *pInstrumentationData = (BYTE*)GetPgoInstrumentationResults->GetBuffer(tempValue.data_index);
    *pPgoSource           = (ICorJitInfo::PgoSource)tempValue.pgoSource;

    ICorJitInfo::PgoInstrumentationSchema* pOutSchema = (ICorJitInfo::PgoInstrumentationSchema*)AllocJitTempBuffer(tempValue.countSchemaItems * sizeof(ICorJitInfo::PgoInstrumentationSchema));

    Agnostic_PgoInstrumentationSchema* pAgnosticSchema = (Agnostic_PgoInstrumentationSchema*)GetPgoInstrumentationResults->GetBuffer(tempValue.schema_index);
    for (UINT32 iSchema = 0; iSchema < tempValue.countSchemaItems; iSchema++)
    {
        pOutSchema[iSchema].Offset              = (size_t)pAgnosticSchema[iSchema].Offset;
        pOutSchema[iSchema].InstrumentationKind = (ICorJitInfo::PgoInstrumentationKind)pAgnosticSchema[iSchema].InstrumentationKind;
        pOutSchema[iSchema].ILOffset            = (int32_t)pAgnosticSchema[iSchema].ILOffset;
        pOutSchema[iSchema].Count               = (int32_t)pAgnosticSchema[iSchema].Count;
        pOutSchema[iSchema].Other               = (int32_t)pAgnosticSchema[iSchema].Other;
    }

    *pSchema = pOutSchema;

    HRESULT result = (HRESULT)tempValue.result;
    return result;
}

void MethodContext::recIsMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, bool result)
{
    if (IsMoreSpecificType == nullptr)
        IsMoreSpecificType = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls1);
    key.B = CastHandle(cls2);

    DWORD value = result ? 1 : 0;
    IsMoreSpecificType->Add(key, value);
    DEBUG_REC(dmpIsMoreSpecificType(key, value));
}
void MethodContext::dmpIsMoreSpecificType(DLDL key, DWORD value)
{
    printf("IsMoreSpecificType key cls1-%016" PRIX64 " cls2-%016" PRIX64 ", value %u", key.A, key.B, value);
}
bool MethodContext::repIsMoreSpecificType(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls1);
    key.B = CastHandle(cls2);

    DWORD value = LookupByKeyOrMiss(IsMoreSpecificType, key, ": key %016" PRIX64 " %016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpIsMoreSpecificType(key, value));
    return value != 0;
}

void MethodContext::recIsExactType(CORINFO_CLASS_HANDLE cls, bool result)
{
    if (IsExactType == nullptr)
        IsExactType = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = result ? 1 : 0;
    IsExactType->Add(key, value);
    DEBUG_REC(dmpIsExactType(key, value));
}
void MethodContext::dmpIsExactType(DWORDLONG key, DWORD value)
{
    printf("IsExactType key cls-%016" PRIX64 ", value res-%u", key, value);
}
bool MethodContext::repIsExactType(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(IsExactType, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsExactType(key, value));
    return value != 0;
}

void MethodContext::recIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE underlyingType, TypeCompareState result)
{
    if (IsEnum == nullptr)
        IsEnum = new LightWeightMap<DWORDLONG, DLD>();

    DWORDLONG key = CastHandle(cls);

    DLD value;
    ZeroMemory(&value, sizeof(value));
    value.A = CastHandle(underlyingType);
    value.B = (DWORD)result;

    IsEnum->Add(key, value);
    DEBUG_REC(dmpIsEnum(key, value));
}
void MethodContext::dmpIsEnum(DWORDLONG key, DLD value)
{
    printf("IsEnum key cls-%016" PRIX64 ", value underlyingType-%016" PRIX64 " result-%u", key, value.A, value.B);
}
TypeCompareState MethodContext::repIsEnum(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE* underlyingType)
{
    DWORDLONG key = CastHandle(cls);

    DLD value = LookupByKeyOrMiss(IsEnum, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpIsEnum(key, value));
    if (underlyingType != nullptr)
        *underlyingType = (CORINFO_CLASS_HANDLE)value.A;
    return (TypeCompareState)value.B;
}

void MethodContext::recGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection, LPVOID result)
{
    if (GetCookieForPInvokeCalliSig == nullptr)
        GetCookieForPInvokeCalliSig = new LightWeightMap<GetCookieForPInvokeCalliSigValue, DLDL>();

    GetCookieForPInvokeCalliSigValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.cbSig      = (DWORD)szMetaSig->cbSig;
    key.pSig_Index = (DWORD)GetCookieForPInvokeCalliSig->AddBuffer((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig);
    key.scope      = CastHandle(szMetaSig->scope);
    key.token      = (DWORD)szMetaSig->token;

    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastPointer(result);

    GetCookieForPInvokeCalliSig->Add(key, value);
    DEBUG_REC(dmpGetCookieForPInvokeCalliSig(key, value));
}
void MethodContext::dmpGetCookieForPInvokeCalliSig(const GetCookieForPInvokeCalliSigValue& key, DLDL value)
{
    printf("GetCookieForPInvokeCalliSig NYI");
}
LPVOID MethodContext::repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection)
{
    GetCookieForPInvokeCalliSigValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.cbSig      = (DWORD)szMetaSig->cbSig;
    key.pSig_Index = (DWORD)GetCookieForPInvokeCalliSig->Contains((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig);
    key.scope      = CastHandle(szMetaSig->scope);
    key.token      = (DWORD)szMetaSig->token;

    DLDL value = LookupByKeyOrMissNoMessage(GetCookieForPInvokeCalliSig, key);

    DEBUG_REP(dmpGetCookieForPInvokeCalliSig(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_VARARGS_HANDLE)value.B;
}

void MethodContext::recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result)
{
    if (CanGetCookieForPInvokeCalliSig == nullptr)
        CanGetCookieForPInvokeCalliSig = new LightWeightMap<CanGetCookieForPInvokeCalliSigValue, DWORD>();

    CanGetCookieForPInvokeCalliSigValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.scope = CastHandle(szMetaSig->scope);
    key.token = (DWORD)szMetaSig->token;

    DWORD value = result ? 1 : 0;
    CanGetCookieForPInvokeCalliSig->Add(key, value);
    DEBUG_REC(dmpCanGetCookieForPInvokeCalliSig(key, value));
}
void MethodContext::dmpCanGetCookieForPInvokeCalliSig(const CanGetCookieForPInvokeCalliSigValue& key, DWORD value)
{
    printf("CanGetCookieForPInvokeCalliSig key scope-%016" PRIX64 " token-%08X, value result-%08X", key.scope, key.token,
           value);
}
bool MethodContext::repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig)
{
    CanGetCookieForPInvokeCalliSigValue key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.scope = CastHandle(szMetaSig->scope);
    key.token = (DWORD)szMetaSig->token;

    DWORD value = LookupByKeyOrMissNoMessage(CanGetCookieForPInvokeCalliSig, key);

    DEBUG_REP(dmpCanGetCookieForPInvokeCalliSig(key, value));
    return value != 0;
}

void MethodContext::recErrorList(const char* error)
{
    if (ErrorList == nullptr)
        ErrorList = new DenseLightWeightMap<DWORD>();

    DWORD temp = (DWORD)-1;

    if (error != nullptr)
        temp = (DWORD)ErrorList->AddBuffer((unsigned char*)error, (DWORD)strlen(error) + 1);

    ErrorList->Append(temp);
}
void MethodContext::dmpErrorList(DWORD key, DWORD value)
{
    printf("ErrorList NYI");
}

void MethodContext::recGetProfilingHandle(bool* pbHookFunction, void** pProfilerHandle, bool* pbIndirectedHandles)
{
    if (GetProfilingHandle == nullptr)
        GetProfilingHandle = new LightWeightMap<DWORD, Agnostic_GetProfilingHandle>();

    Agnostic_GetProfilingHandle value;
    ZeroMemory(&value, sizeof(value)); // Zero value including any struct padding
    value.bHookFunction      = (DWORD)*pbHookFunction;
    value.ProfilerHandle     = CastPointer(*pProfilerHandle);
    value.bIndirectedHandles = (DWORD)*pbIndirectedHandles;
    GetProfilingHandle->Add(0, value);
    DEBUG_REC(dmpGetProfilingHandle(0, value));
}
void MethodContext::dmpGetProfilingHandle(DWORD key, const Agnostic_GetProfilingHandle& value)
{
    printf("GetProfilingHandle key %u, value bHookFtn-%u profHnd-%016" PRIX64 " bIndHnd-%u", key, value.bHookFunction,
           value.ProfilerHandle, value.bIndirectedHandles);
}
void MethodContext::repGetProfilingHandle(bool* pbHookFunction, void** pProfilerHandle, bool* pbIndirectedHandles)
{
    Agnostic_GetProfilingHandle value = LookupByKeyOrMissNoMessage(GetProfilingHandle, 0);

    DEBUG_REP(dmpGetProfilingHandle(0, value));

    *pbHookFunction      = value.bHookFunction != 0;
    *pProfilerHandle     = (void*)value.ProfilerHandle;
    *pbIndirectedHandles = value.bIndirectedHandles != 0;
}

void MethodContext::recEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void** ppIndirection, CORINFO_FIELD_HANDLE result)
{
    if (EmbedFieldHandle == nullptr)
        EmbedFieldHandle = new LightWeightMap<DWORDLONG, DLDL>();

    DLDL value;
    if (ppIndirection != nullptr)
        value.A = CastPointer(*ppIndirection);
    else
        value.A = 0;
    value.B     = CastHandle(result);

    DWORDLONG key = CastHandle(handle);
    EmbedFieldHandle->Add(key, value);
    DEBUG_REC(dmpEmbedFieldHandle(key, value));
}
void MethodContext::dmpEmbedFieldHandle(DWORDLONG key, DLDL value)
{
    printf("EmbedFieldHandle NYI");
}
CORINFO_FIELD_HANDLE MethodContext::repEmbedFieldHandle(CORINFO_FIELD_HANDLE handle, void** ppIndirection)
{
    DWORDLONG key = CastHandle(handle);
    DLDL value = LookupByKeyOrMiss(EmbedFieldHandle, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpEmbedFieldHandle(key, value));

    if (ppIndirection != nullptr)
        *ppIndirection = (void*)value.A;
    return (CORINFO_FIELD_HANDLE)value.B;
}

void MethodContext::recCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass,
                                           CORINFO_CLASS_HANDLE toClass,
                                           TypeCompareState     result)
{
    if (CompareTypesForCast == nullptr)
        CompareTypesForCast = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(fromClass);
    key.B = CastHandle(toClass);

    DWORD value = (DWORD)result;
    CompareTypesForCast->Add(key, value);
    DEBUG_REC(dmpCompareTypesForCast(key, value));
}
void MethodContext::dmpCompareTypesForCast(DLDL key, DWORD value)
{
    printf("CompareTypesForCast key fromClass=%016" PRIX64 ", toClass=%016" PRIx64 ", result=%d", key.A, key.B, value);
}
TypeCompareState MethodContext::repCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(fromClass);
    key.B = CastHandle(toClass);

    DWORD value = LookupByKeyOrMiss(CompareTypesForCast, key, ": key %016" PRIX64 " %016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpCompareTypesForCast(key, value));
    TypeCompareState result = (TypeCompareState)value;
    return result;
}

void MethodContext::recCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1,
                                               CORINFO_CLASS_HANDLE cls2,
                                               TypeCompareState     result)
{
    if (CompareTypesForEquality == nullptr)
        CompareTypesForEquality = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls1);
    key.B = CastHandle(cls2);

    DWORD value = (DWORD)result;
    CompareTypesForEquality->Add(key, value);
    DEBUG_REC(dmpCompareTypesForEquality(key, value));
}
void MethodContext::dmpCompareTypesForEquality(DLDL key, DWORD value)
{
    printf("CompareTypesForEquality key cls1=%016" PRIX64 ", cls2=%016" PRIx64 ", result=%d", key.A, key.B, value);
}
TypeCompareState MethodContext::repCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls1);
    key.B = CastHandle(cls2);

    DWORD value = LookupByKeyOrMiss(CompareTypesForEquality, key, ": key %016" PRIX64 " %016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpCompareTypesForEquality(key, value));
    TypeCompareState result = (TypeCompareState)value;
    return result;
}

void MethodContext::recGetSystemVAmd64PassStructInRegisterDescriptor(
    CORINFO_CLASS_HANDLE                                 structHnd,
    SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr,
    bool                                                 result)
{
    if (GetSystemVAmd64PassStructInRegisterDescriptor == nullptr)
        GetSystemVAmd64PassStructInRegisterDescriptor =
            new LightWeightMap<DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor>();

    DWORDLONG                                              key;
    Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;

    key = CastHandle(structHnd);

    value.passedInRegisters = (DWORD)structPassInRegDescPtr->passedInRegisters;
    value.eightByteCount    = (DWORD)structPassInRegDescPtr->eightByteCount;
    for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
    {
        value.eightByteClassifications[i] = (DWORD)structPassInRegDescPtr->eightByteClassifications[i];
        value.eightByteSizes[i]           = (DWORD)structPassInRegDescPtr->eightByteSizes[i];
        value.eightByteOffsets[i]         = (DWORD)structPassInRegDescPtr->eightByteOffsets[i];
    }
    value.result = result ? 1 : 0;

    GetSystemVAmd64PassStructInRegisterDescriptor->Add(key, value);
    DEBUG_REC(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));
}
void MethodContext::dmpGetSystemVAmd64PassStructInRegisterDescriptor(
    DWORDLONG key, const Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor& value)
{
    printf("GetSystemVAmd64PassStructInRegisterDescriptor key structHnd-%016" PRIX64 ", value passInReg-%u 8bCount-%u", key,
           value.passedInRegisters, value.eightByteCount);
    for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
    {
        printf(" 8bClass[%u]-%u 8bSz[%u]-%u 8bOff[%u]-%u", i, value.eightByteClassifications[i], i,
               value.eightByteSizes[i], i, value.eightByteOffsets[i]);
    }
    printf(" result %u", value.result);
}
bool MethodContext::repGetSystemVAmd64PassStructInRegisterDescriptor(
    CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
{
    DWORDLONG key = CastHandle(structHnd);

    Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor value;
    value = LookupByKeyOrMiss(GetSystemVAmd64PassStructInRegisterDescriptor, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetSystemVAmd64PassStructInRegisterDescriptor(key, value));

    structPassInRegDescPtr->passedInRegisters = value.passedInRegisters ? true : false;
    structPassInRegDescPtr->eightByteCount    = (unsigned __int8)value.eightByteCount;
    for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
    {
        structPassInRegDescPtr->eightByteClassifications[i] =
            (SystemVClassificationType)value.eightByteClassifications[i];
        structPassInRegDescPtr->eightByteSizes[i]   = (unsigned __int8)value.eightByteSizes[i];
        structPassInRegDescPtr->eightByteOffsets[i] = (unsigned __int8)value.eightByteOffsets[i];
    }

    return value.result ? true : false;
}

void MethodContext::recGetSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering)
{
    if (GetSwiftLowering == nullptr)
        GetSwiftLowering = new LightWeightMap<DWORDLONG, Agnostic_GetSwiftLowering>();

    DWORDLONG key = CastHandle(structHnd);

    Agnostic_GetSwiftLowering value;
    ZeroMemory(&value, sizeof(value));
    value.byReference = pLowering->byReference ? 1 : 0;
    if (!pLowering->byReference)
    {
        value.numLoweredElements = static_cast<DWORD>(pLowering->numLoweredElements);
        for (size_t i = 0; i < pLowering->numLoweredElements; i++)
        {
            value.loweredElements[i] = static_cast<DWORD>(pLowering->loweredElements[i]);
            value.offsets[i] = pLowering->offsets[i];
        }
    }

    GetSwiftLowering->Add(key, value);
    DEBUG_REC(dmpGetSwiftLowering(key, value));
}
void MethodContext::dmpGetSwiftLowering(
    DWORDLONG key, const Agnostic_GetSwiftLowering& value)
{
    printf("GetSwiftLowering key structHnd-%016" PRIX64 ", value byReference-%u numLoweredElements-%u", key,
        value.byReference, value.numLoweredElements);
    for (size_t i = 0; i < value.numLoweredElements; i++)
    {
        printf(" [%zu] %u", i, value.loweredElements[i]);
    }
}
void MethodContext::repGetSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering)
{
    DWORDLONG key = CastHandle(structHnd);
    Agnostic_GetSwiftLowering value = LookupByKeyOrMiss(GetSwiftLowering, key, ": key %016" PRIX64 "", key);

    DEBUG_REP(dmpGetSwiftLowering(key, value));

    pLowering->byReference = value.byReference != 0;
    pLowering->numLoweredElements = value.numLoweredElements;

    for (size_t i = 0; i < pLowering->numLoweredElements; i++)
    {
        pLowering->loweredElements[i] = static_cast<CorInfoType>(value.loweredElements[i]);
        pLowering->offsets[i] = value.offsets[i];
    }
}

void MethodContext::recGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value)
{
    if (GetLoongArch64PassStructInRegisterFlags == nullptr)
        GetLoongArch64PassStructInRegisterFlags = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(structHnd);

    GetLoongArch64PassStructInRegisterFlags->Add(key, value);
    DEBUG_REC(dmpGetLoongArch64PassStructInRegisterFlags(key, value));
}

void MethodContext::dmpGetLoongArch64PassStructInRegisterFlags(DWORDLONG key, DWORD value)
{
    printf("GetLoongArch64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value);
}

DWORD MethodContext::repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd)
{
    DWORDLONG key = CastHandle(structHnd);

    DWORD value = LookupByKeyOrMissNoMessage(GetLoongArch64PassStructInRegisterFlags, key);
    DEBUG_REP(dmpGetLoongArch64PassStructInRegisterFlags(key, value));
    return value;
}

void MethodContext::recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value)
{
    if (GetRISCV64PassStructInRegisterFlags == nullptr)
        GetRISCV64PassStructInRegisterFlags = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(structHnd);

    GetRISCV64PassStructInRegisterFlags->Add(key, value);
    DEBUG_REC(dmpGetRISCV64PassStructInRegisterFlags(key, value));
}

void MethodContext::dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value)
{
    printf("GetRISCV64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value);
}

DWORD MethodContext::repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd)
{
    DWORDLONG key = CastHandle(structHnd);

    DWORD value = LookupByKeyOrMissNoMessage(GetRISCV64PassStructInRegisterFlags, key);
    DEBUG_REP(dmpGetRISCV64PassStructInRegisterFlags(key, value));
    return value;
}

void MethodContext::recGetRelocTypeHint(void* target, WORD result)
{
    if (GetRelocTypeHint == nullptr)
        GetRelocTypeHint = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastPointer(target);
    DWORD value = (DWORD)result;
    GetRelocTypeHint->Add(key, value);
    DEBUG_REC(dmpGetRelocTypeHint(key, value));
}
void MethodContext::dmpGetRelocTypeHint(DWORDLONG key, DWORD value)
{
    printf("GetRelocTypeHint key tgt-%016" PRIX64 ", value hint-%u", key, value);
}
WORD MethodContext::repGetRelocTypeHint(void* target)
{
    DWORDLONG key = CastPointer(target);

    if ((GetRelocTypeHint == nullptr) || (GetRelocTypeHint->GetIndex(key) == -1))
    {
#ifdef sparseMC
        LogDebug("Sparse - repGetRelocTypeHint yielding fake answer...");
        return 65535;
#else
        LogException(EXCEPTIONCODE_MC, "Didn't find %016" PRIX64 "", key);
#endif
    }

    int  index  = GetRelocTypeHint->GetIndex(key);
    WORD retVal = 0;
    if (index == -1)
    {
        retVal = IMAGE_REL_BASED_REL32;
    }
    else
    {
        retVal = (WORD)GetRelocTypeHint->Get(key);
    }

    DEBUG_REP(dmpGetRelocTypeHint(key, (DWORD)retVal));
    return retVal;
}

void MethodContext::recGetExpectedTargetArchitecture(DWORD result)
{
    if (GetExpectedTargetArchitecture == nullptr)
        GetExpectedTargetArchitecture = new LightWeightMap<DWORD, DWORD>();

    DWORD key = 0; // There is only ever a single entry to this map
    GetExpectedTargetArchitecture->Add(key, result);
    DEBUG_REC(dmpGetExpectedTargetArchitecture(key, result));
}
void MethodContext::dmpGetExpectedTargetArchitecture(DWORD key, DWORD result)
{
    printf("GetExpectedTargetArchitecture key %u, res %u", key, result);
}
DWORD MethodContext::repGetExpectedTargetArchitecture()
{
    DWORD key = 0;

    DWORD value = LookupByKeyOrMiss(GetExpectedTargetArchitecture, key, ": key %08X", key);

    DEBUG_REP(dmpGetExpectedTargetArchitecture(key, value));
    return value;
}

void MethodContext::recDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls, bool result)
{
    if (DoesFieldBelongToClass == nullptr)
        DoesFieldBelongToClass = new LightWeightMap<DLDL, DWORD>();

    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(fld);
    key.B = CastHandle(cls);

    DWORD value = (DWORD)result;
    DoesFieldBelongToClass->Add(key, value);
    DEBUG_REC(dmpDoesFieldBelongToClass(key, result));
}

void MethodContext::dmpDoesFieldBelongToClass(DLDL key, bool value)
{
    printf("DoesFieldBelongToClass key fld=%016" PRIX64 ", cls=%016" PRIx64 ", result=%d", key.A, key.B, value);
}

bool MethodContext::repDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls)
{
    DLDL key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(fld);
    key.B = CastHandle(cls);

    DWORD value = LookupByKeyOrMiss(DoesFieldBelongToClass, key, ": key %016" PRIX64 " %016" PRIX64 "", key.A, key.B);

    DEBUG_REP(dmpDoesFieldBelongToClass(key, value));
    return value != 0;
}

void MethodContext::recGetClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, char* className, const char** namespaceName)
{
    if (GetClassNameFromMetadata == nullptr)
        GetClassNameFromMetadata = new LightWeightMap<DLD, DD>();

    DLD key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.A = CastHandle(cls);
    key.B = (namespaceName != nullptr);

    DD  value;
    if (className != nullptr)
        value.A = GetClassNameFromMetadata->AddBuffer((unsigned char*)className, (DWORD)strlen(className) + 1);
    else
        value.A = (DWORD)-1;

    if ((namespaceName != nullptr) && (*namespaceName != nullptr))
        value.B =
            GetClassNameFromMetadata->AddBuffer((unsigned char*)*namespaceName, (DWORD)strlen(*namespaceName) + 1);
    else
        value.B = (DWORD)-1;

    GetClassNameFromMetadata->Add(key, value);
    DEBUG_REC(dmpGetClassNameFromMetadata(key, value));
}

void MethodContext::dmpGetClassNameFromMetadata(DLD key, DD value)
{
    unsigned char* className     = (unsigned char*)GetClassNameFromMetadata->GetBuffer(value.A);
    unsigned char* namespaceName = (unsigned char*)GetClassNameFromMetadata->GetBuffer(value.B);
    printf("GetClassNameFromMetadata key - classNonNull-%" PRIu64 " namespaceNonNull-%u, value "
           "class-'%s', namespace-'%s'",
           key.A, key.B, className, namespaceName);
    GetClassNameFromMetadata->Unlock();
}

const char* MethodContext::repGetClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, const char** namespaceName)
{
    const char* result = nullptr;
    DLD         key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(cls);
    key.B = (namespaceName != nullptr);

    DD value = LookupByKeyOrMiss(GetClassNameFromMetadata, key, " : key cls-%016" PRIX64 " hasNs-%u", key.A, key.B);
    DEBUG_REP(dmpGetClassNameFromMetadata(key, value));

    result = (const char*)GetClassNameFromMetadata->GetBuffer(value.A);

    if (namespaceName != nullptr)
    {
        *namespaceName = (const char*)GetClassNameFromMetadata->GetBuffer(value.B);
    }
    return result;
}

void MethodContext::recGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls,
                                                    unsigned             index,
                                                    CORINFO_CLASS_HANDLE result)
{
    if (GetTypeInstantiationArgument == nullptr)
        GetTypeInstantiationArgument = new LightWeightMap<DLD, DWORDLONG>();

    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(cls);
    key.B = index;
    DWORDLONG value = CastHandle(result);
    GetTypeInstantiationArgument->Add(key, value);
    DEBUG_REC(dmpGetTypeInstantiationArgument(key, value));
}
void MethodContext::dmpGetTypeInstantiationArgument(DLD key, DWORDLONG value)
{
    printf("GetTypeInstantiationArgument key - classNonNull-%" PRIu64 ", index-%u, value NonNull-%" PRIu64 "", key.A, key.B, value);
    GetTypeInstantiationArgument->Unlock();
}
CORINFO_CLASS_HANDLE MethodContext::repGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index)
{
    DLD key;
    ZeroMemory(&key, sizeof(key));
    key.A = CastHandle(cls);
    key.B = index;

    DWORDLONG value = LookupByKeyOrMissNoMessage(GetTypeInstantiationArgument, key);
    DEBUG_REP(dmpGetTypeInstantiationArgument(key, value));
    return (CORINFO_CLASS_HANDLE)value;
}

void MethodContext::recPrint(
    const char* name,
    LightWeightMap<DWORDLONG, Agnostic_PrintResult>*& map,
    DWORDLONG handle,
    char* buffer,
    size_t bufferSize,
    size_t* pRequiredBufferSize,
    size_t bytesWritten)
{
    if (map == nullptr)
        map = new LightWeightMap<DWORDLONG, Agnostic_PrintResult>();

    // Required size of a buffer that contains all data and null terminator.
    UINT requiredBufferSize = UINT_MAX;
    if (pRequiredBufferSize != nullptr)
    {
        requiredBufferSize = (UINT)(*pRequiredBufferSize);
    }
    else if (bytesWritten + 1 < bufferSize)
    {
        requiredBufferSize = (UINT)(bytesWritten + 1);
    }

    Agnostic_PrintResult res;
    int index = map->GetIndex(handle);
    if (index != -1)
    {
        // Merge with existing entry

        res = map->GetItem(index);

        if (requiredBufferSize != UINT_MAX)
        {
            res.requiredBufferSize = requiredBufferSize;
        }

        if (bytesWritten > res.stringBufferSize)
        {
            // Always stored without null terminator.
            res.stringBuffer = map->AddBuffer((unsigned char*)buffer, static_cast<unsigned>(bytesWritten));
            res.stringBufferSize = (UINT)bytesWritten;
        }

        map->Update(index, res);

        DEBUG_REC(dmpPrint(name, map, handle, res));
        return;
    }

    if (buffer != nullptr)
    {
        res.stringBuffer = map->AddBuffer((unsigned char*)buffer, static_cast<unsigned>(bytesWritten));
    }
    else
    {
        res.stringBuffer = UINT_MAX;
    }

    res.stringBufferSize = (UINT)bytesWritten;
    res.requiredBufferSize = requiredBufferSize;

    map->Add(handle, res);
    DEBUG_REC(dmpPrint(name, map, handle, res));
}

void MethodContext::dmpPrint(
    const char* name,
    LightWeightMapBuffer* buffer,
    DWORDLONG key,
    const Agnostic_PrintResult& value)
{
    printf("%s key hnd-%016" PRIX64 ", stringBufferSize-%u, requiredBufferSize-%u", name, key, value.stringBufferSize, value.requiredBufferSize);
    buffer->Unlock();
}

size_t MethodContext::repPrint(
    const char* name,
    LightWeightMap<DWORDLONG, Agnostic_PrintResult>*& map,
    DWORDLONG handle,
    char* buffer,
    size_t bufferSize,
    size_t* pRequiredBufferSize)
{
    Agnostic_PrintResult res = LookupByKeyOrMiss(map, handle, ": map %s key %016" PRIx64 "", name, handle);
    DEBUG_REP(dmpPrint(name, buffer, handle, res));

    if (pRequiredBufferSize != nullptr)
    {
        if (res.requiredBufferSize == UINT_MAX)
        {
            LogException(EXCEPTIONCODE_MC, "SuperPMI assertion failed (missing requiredBufferSize for %s key %016" PRIx64 ")", name, handle);
        }

        *pRequiredBufferSize = res.requiredBufferSize;
    }

    // requiredBufferSize is with null terminator, but buffer is stored without
    // null terminator. Determine if we have enough data to answer the query
    // losslessly.
    // Note that requiredBufferSize is always set by recording side if we had
    // enough information to determine it.
    bool haveFullBuffer = (res.requiredBufferSize != UINT_MAX) && ((res.stringBufferSize + 1) >= res.requiredBufferSize);
    if (!haveFullBuffer && (bufferSize > static_cast<size_t>(res.stringBufferSize) + 1))
    {
        LogException(EXCEPTIONCODE_MC, "SuperPMI assertion failed (not enough buffer data for %s key %016" PRIx64 ")", name, handle);
    }

    size_t bytesWritten = 0;
    if ((buffer != nullptr) && (bufferSize > 0))
    {
        bytesWritten = min(bufferSize - 1, res.stringBufferSize);
        if (bytesWritten > 0)
        {
            // The "full buffer" check above ensures this given that
            // res.stringBuffer == UINT_MAX implies res.stringBufferSize == 0.
            Assert(res.stringBuffer != UINT_MAX);
            char* storedBuffer = (char*)map->GetBuffer(res.stringBuffer);
            memcpy(buffer, storedBuffer, bytesWritten);
        }
        buffer[bytesWritten] = '\0';
    }

    return bytesWritten;
}

void MethodContext::recPrintObjectDescription(CORINFO_OBJECT_HANDLE handle, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize, size_t bytesWritten)
{
    recPrint("PrintObjectDescription", PrintObjectDescription, CastHandle(handle), buffer, bufferSize, pRequiredBufferSize, bytesWritten);
}
void MethodContext::dmpPrintObjectDescription(DWORDLONG key, const Agnostic_PrintResult& value)
{
    dmpPrint("PrintObjectDescription", PrintObjectDescription, key, value);
}
size_t MethodContext::repPrintObjectDescription(CORINFO_OBJECT_HANDLE handle, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize)
{
    return repPrint("PrintObjectDescription", PrintObjectDescription, CastHandle(handle), buffer, bufferSize, pRequiredBufferSize);
}

void MethodContext::recPrintClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize, size_t bytesWritten)
{
    recPrint("PrintClassName", PrintClassName, CastHandle(cls), buffer, bufferSize, pRequiredBufferSize, bytesWritten);
}
void MethodContext::dmpPrintClassName(DWORDLONG key, const Agnostic_PrintResult& value)
{
    dmpPrint("PrintClassName", PrintClassName, key, value);
}
size_t MethodContext::repPrintClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize)
{
    return repPrint("PrintClassName", PrintClassName, CastHandle(cls), buffer, bufferSize, pRequiredBufferSize);
}

void MethodContext::recPrintFieldName(CORINFO_FIELD_HANDLE fld, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize, size_t bytesWritten)
{
    recPrint("PrintFieldName", PrintFieldName, CastHandle(fld), buffer, bufferSize, pRequiredBufferSize, bytesWritten);
}
void MethodContext::dmpPrintFieldName(DWORDLONG key, const Agnostic_PrintResult& value)
{
    dmpPrint("PrintFieldName", PrintFieldName, key, value);
}
size_t MethodContext::repPrintFieldName(CORINFO_FIELD_HANDLE fld, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize)
{
    return repPrint("PrintFieldName", PrintFieldName, CastHandle(fld), buffer, bufferSize, pRequiredBufferSize);
}

void MethodContext::recPrintMethodName(CORINFO_METHOD_HANDLE meth, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize, size_t bytesWritten)
{
    recPrint("PrintMethodName", PrintMethodName, CastHandle(meth), buffer, bufferSize, pRequiredBufferSize, bytesWritten);
}
void MethodContext::dmpPrintMethodName(DWORDLONG key, const Agnostic_PrintResult& value)
{
    dmpPrint("PrintMethodName", PrintMethodName, key, value);
}
size_t MethodContext::repPrintMethodName(CORINFO_METHOD_HANDLE meth, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize)
{
    return repPrint("PrintMethodName", PrintMethodName, CastHandle(meth), buffer, bufferSize, pRequiredBufferSize);
}

void MethodContext::recGetTailCallHelpers(
    CORINFO_RESOLVED_TOKEN* callToken,
    CORINFO_SIG_INFO* sig,
    CORINFO_GET_TAILCALL_HELPERS_FLAGS flags,
    CORINFO_TAILCALL_HELPERS* pResult)
{
    if (GetTailCallHelpers == nullptr)
        GetTailCallHelpers = new LightWeightMap<Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS>();

    Agnostic_GetTailCallHelpers key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.callToken = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(callToken, GetTailCallHelpers);
    key.sig       = SpmiRecordsHelper::StoreAgnostic_CORINFO_SIG_INFO(*sig, GetTailCallHelpers, SigInstHandleMap);
    key.flags     = (DWORD)flags;

    Agnostic_CORINFO_TAILCALL_HELPERS value;
    ZeroMemory(&value, sizeof(value));
    value.result = pResult != nullptr;
    if (pResult != nullptr)
    {
        value.flags       = (DWORD)pResult->flags;
        value.hStoreArgs  = CastHandle(pResult->hStoreArgs);
        value.hCallTarget = CastHandle(pResult->hCallTarget);
        value.hDispatcher = CastHandle(pResult->hDispatcher);
    }

    GetTailCallHelpers->Add(key, value);
    DEBUG_REC(dmpGetTailCallHelpers(key, value));
}

void MethodContext::dmpGetTailCallHelpers(const Agnostic_GetTailCallHelpers& key, const Agnostic_CORINFO_TAILCALL_HELPERS& value)
{
    printf("GetTailCallHelpers key callToken-%s sig-%s flg-%08X",
        SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.callToken).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_SIG_INFO(key.sig, GetTailCallHelpers, SigInstHandleMap).c_str(),
        key.flags);
    printf(", value result-%s flg-%08X hStoreArgs-%016" PRIX64 " hCallTarget-%016" PRIX64 " hDispatcher-%016" PRIX64 "",
        value.result ? "true" : "false",
        value.flags,
        value.hStoreArgs,
        value.hCallTarget,
        value.hDispatcher);
}

bool MethodContext::repGetTailCallHelpers(
    CORINFO_RESOLVED_TOKEN* callToken,
    CORINFO_SIG_INFO* sig,
    CORINFO_GET_TAILCALL_HELPERS_FLAGS flags,
    CORINFO_TAILCALL_HELPERS* pResult)
{
    AssertMapExistsNoMessage(GetTailCallHelpers);

    Agnostic_GetTailCallHelpers key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.callToken = SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(callToken, GetTailCallHelpers);
    key.sig       = SpmiRecordsHelper::RestoreAgnostic_CORINFO_SIG_INFO(*sig, GetTailCallHelpers, SigInstHandleMap);
    key.flags     = (DWORD)flags;

    Agnostic_CORINFO_TAILCALL_HELPERS value = LookupByKeyOrMissNoMessage(GetTailCallHelpers, key);

    DEBUG_REP(dmpGetTailCallHelpers(key, value));

    if (!value.result)
        return false;

    pResult->flags       = (CORINFO_TAILCALL_HELPERS_FLAGS)value.flags;
    pResult->hStoreArgs  = (CORINFO_METHOD_HANDLE)value.hStoreArgs;
    pResult->hCallTarget = (CORINFO_METHOD_HANDLE)value.hCallTarget;
    pResult->hDispatcher = (CORINFO_METHOD_HANDLE)value.hDispatcher;
    return true;
}

void MethodContext::recUpdateEntryPointForTailCall(
    const CORINFO_CONST_LOOKUP& origEntryPoint,
    const CORINFO_CONST_LOOKUP& newEntryPoint)
{
    if (UpdateEntryPointForTailCall == nullptr)
        UpdateEntryPointForTailCall = new LightWeightMap<Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP>();

    Agnostic_CORINFO_CONST_LOOKUP key = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&origEntryPoint);
    Agnostic_CORINFO_CONST_LOOKUP value = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&newEntryPoint);
    UpdateEntryPointForTailCall->Add(key, value);
    DEBUG_REC(dmpUpdateEntryPointForTailCall(key, value));
}

void MethodContext::dmpUpdateEntryPointForTailCall(
    const Agnostic_CORINFO_CONST_LOOKUP& origEntryPoint,
    const Agnostic_CORINFO_CONST_LOOKUP& newEntryPoint)
{
    printf("UpdateEntryPointForTailcall orig=%s new=%s",
        SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(origEntryPoint).c_str(),
        SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(newEntryPoint).c_str());
}

void MethodContext::repUpdateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint)
{
    AssertMapExistsNoMessage(UpdateEntryPointForTailCall);

    Agnostic_CORINFO_CONST_LOOKUP key = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(entryPoint);
    Agnostic_CORINFO_CONST_LOOKUP value = LookupByKeyOrMissNoMessage(UpdateEntryPointForTailCall, key);

    DEBUG_REP(dmpUpdateEntryPointForTailCall(key, value));

    *entryPoint = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value);
}

void MethodContext::recGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod, mdMethodDef result)
{
    if (GetMethodDefFromMethod == nullptr)
        GetMethodDefFromMethod = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(hMethod);
    DWORD value = (DWORD)result;
    GetMethodDefFromMethod->Add(key, value);
    DEBUG_REC(dmpGetMethodDefFromMethod(key, value));
}
void MethodContext::dmpGetMethodDefFromMethod(DWORDLONG key, DWORD value)
{
    printf("GetMethodDefFromMethod key ftn-%016" PRIX64 ", value res-%u", key, value);
}
mdMethodDef MethodContext::repGetMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod)
{
    // Since this is diagnostic, fake up a result if one wasn't recorded.
    if (GetMethodDefFromMethod == nullptr)
        return (mdMethodDef)0x06000000;

    DWORDLONG key = CastHandle(hMethod);

    int index = GetMethodDefFromMethod->GetIndex(key);
    if (index < 0)
        return (mdMethodDef)0x06000001;

    DWORD value = GetMethodDefFromMethod->Get(key);
    DEBUG_REP(dmpGetMethodDefFromMethod(key, value));
    return (mdMethodDef)value;
}

void MethodContext::recCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, bool fOptional, bool result)
{
    if (CheckMethodModifier == nullptr)
        CheckMethodModifier = new LightWeightMap<Agnostic_CheckMethodModifier, DWORD>();

    Agnostic_CheckMethodModifier key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hMethod = CastHandle(hMethod);
    // If the input matches something already in the buffer, just re-use that slot.. easier than searching for a soft
    // key on rep.
    if (modifier != nullptr)
        key.modifier =
            (DWORD)CheckMethodModifier->AddBuffer((unsigned char*)modifier, (unsigned int)strlen(modifier) + 1);
    else
        key.modifier = (DWORD)-1;

    key.fOptional = (DWORD)fOptional;

    DWORD value = result ? 1 : 0;
    CheckMethodModifier->Add(key, value);
    DEBUG_REC(dmpCheckMethodModifier(key, value));
}
void MethodContext::dmpCheckMethodModifier(const Agnostic_CheckMethodModifier& key, DWORD value)
{
    printf("CheckMethodModifier key, ftn-%016" PRIX64 " mod-'%s' opt-%u, value res-%u", key.hMethod,
           (unsigned char*)CheckMethodModifier->GetBuffer(key.modifier), key.fOptional, value);
    CheckMethodModifier->Unlock();
}
bool MethodContext::repCheckMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, bool fOptional)
{
    Agnostic_CheckMethodModifier key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
    key.hMethod = CastHandle(hMethod);
    if (modifier != nullptr)
        key.modifier =
            (DWORD)CheckMethodModifier->Contains((unsigned char*)modifier, (unsigned int)strlen(modifier) + 1);
    else
        key.modifier = (DWORD)-1;

    key.fOptional = (DWORD)fOptional;

    DWORD value = LookupByKeyOrMissNoMessage(CheckMethodModifier, key);

    DEBUG_REP(dmpCheckMethodModifier(key, value));
    return value != 0;
}

void MethodContext::recGetArrayRank(CORINFO_CLASS_HANDLE cls, unsigned result)
{
    if (GetArrayRank == nullptr)
        GetArrayRank = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(cls);
    DWORD value = (DWORD)result;
    GetArrayRank->Add(key, value);
    DEBUG_REC(dmpGetArrayRank(key, value));
}
void MethodContext::dmpGetArrayRank(DWORDLONG key, DWORD value)
{
    printf("GetArrayRank key %016" PRIX64 ", value %u", key, value);
}
unsigned MethodContext::repGetArrayRank(CORINFO_CLASS_HANDLE cls)
{
    DWORDLONG key = CastHandle(cls);
    DWORD value = LookupByKeyOrMiss(GetArrayRank, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetArrayRank(key, value));
    unsigned result = (unsigned)value;
    return result;
}

void MethodContext::recGetArrayIntrinsicID(CORINFO_METHOD_HANDLE hMethod, CorInfoArrayIntrinsic result)
{
    if (GetArrayIntrinsicID == nullptr)
        GetArrayIntrinsicID = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(hMethod);
    DWORD value = (DWORD)result;
    GetArrayIntrinsicID->Add(key, value);
    DEBUG_REC(dmpGetArrayIntrinsicID(key, value));
}
void MethodContext::dmpGetArrayIntrinsicID(DWORDLONG key, DWORD value)
{
    printf("GetArrayIntrinsicID key %016" PRIX64 ", value %u", key, value);
}
CorInfoArrayIntrinsic MethodContext::repGetArrayIntrinsicID(CORINFO_METHOD_HANDLE hMethod)
{
    DWORDLONG key = CastHandle(hMethod);
    DWORD value = LookupByKeyOrMiss(GetArrayIntrinsicID, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetArrayIntrinsicID(key, value));
    CorInfoArrayIntrinsic result = (CorInfoArrayIntrinsic)value;
    return result;
}

void MethodContext::recIsFieldStatic(CORINFO_FIELD_HANDLE fhld, bool result)
{
    if (IsFieldStatic == nullptr)
        IsFieldStatic = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(fhld);
    DWORD value = result ? 1 : 0;
    IsFieldStatic->Add(key, value);
    DEBUG_REC(dmpIsFieldStatic(key, value));
}
void MethodContext::dmpIsFieldStatic(DWORDLONG key, DWORD value)
{
    printf("IsFieldStatic key %016" PRIX64 ", value %u", key, value);
}
bool MethodContext::repIsFieldStatic(CORINFO_FIELD_HANDLE fhld)
{
    DWORDLONG key = CastHandle(fhld);
    DWORD value = LookupByKeyOrMiss(IsFieldStatic, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpIsFieldStatic(key, value));
    return value != 0;
}

void MethodContext::recGetArrayOrStringLength(CORINFO_OBJECT_HANDLE objHandle, int result)
{
    if (GetArrayOrStringLength == nullptr)
        GetArrayOrStringLength = new LightWeightMap<DWORDLONG, DWORD>();

    DWORDLONG key = CastHandle(objHandle);
    DWORD value = (DWORD)result;
    GetArrayOrStringLength->Add(key, value);
    DEBUG_REC(dmpGetArrayOrStringLength(key, value));
}
void MethodContext::dmpGetArrayOrStringLength(DWORDLONG key, DWORD value)
{
    printf("GetArrayOrStringLength key %016" PRIX64 ", value %u", key, value);
}
int MethodContext::repGetArrayOrStringLength(CORINFO_OBJECT_HANDLE objHandle)
{
    DWORDLONG key = CastHandle(objHandle);
    DWORD value = LookupByKeyOrMiss(GetArrayOrStringLength, key, ": key %016" PRIX64 "", key);
    DEBUG_REP(dmpGetArrayOrStringLength(key, value));
    return (int)value;
}

void MethodContext::recGetIntConfigValue(const WCHAR* name, int defaultValue, int result)
{
    if (GetIntConfigValue == nullptr)
        GetIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>();

    AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");

    Agnostic_ConfigIntInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding

    DWORD index =
        (DWORD)GetIntConfigValue->AddBuffer((unsigned char*)name, sizeof(WCHAR) * ((unsigned int)u16_strlen(name) + 1));

    key.nameIndex    = index;
    key.defaultValue = defaultValue;

    GetIntConfigValue->Add(key, result);
    DEBUG_REC(dmpGetIntConfigValue(key, result));
}

void MethodContext::dmpGetIntConfigValue(const Agnostic_ConfigIntInfo& key, int value)
{
    const WCHAR* name    = (const WCHAR*)GetIntConfigValue->GetBuffer(key.nameIndex);
    std::string nameUtf8 = ConvertToUtf8(name);
    printf("GetIntConfigValue name %s, default value %d, value %d", nameUtf8.c_str(), key.defaultValue, value);
    GetIntConfigValue->Unlock();
}

int MethodContext::repGetIntConfigValue(const WCHAR* name, int defaultValue)
{
    if (ignoreStoredConfig)
        return defaultValue;

    if (GetIntConfigValue == nullptr)
        return defaultValue;

    AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");

    Agnostic_ConfigIntInfo key;
    ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding

    size_t nameLenInBytes = sizeof(WCHAR) * (u16_strlen(name) + 1);
    int    nameIndex      = GetIntConfigValue->Contains((unsigned char*)name, (unsigned int)nameLenInBytes);
    if (nameIndex == -1) // config name not in map
        return defaultValue;

    key.nameIndex    = (DWORD)nameIndex;
    key.defaultValue = defaultValue;

    int index = GetIntConfigValue->GetIndex(key);
    if (index == -1)
    {
        // default value has changed
        return defaultValue;
    }

    DWORD value = GetIntConfigValue->GetItem(index);
    DEBUG_REP(dmpGetIntConfigValue(key, value));
    return (int)value;
}

void MethodContext::recGetStringConfigValue(const WCHAR* name, const WCHAR* result)
{
    if (GetStringConfigValue == nullptr)
        GetStringConfigValue = new LightWeightMap<DWORD, DWORD>();

    AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");

    DWORD nameIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)name,
                                                             sizeof(WCHAR) * ((unsigned int)u16_strlen(name) + 1));

    DWORD resultIndex = (DWORD)-1;
    if (result != nullptr)
        resultIndex = (DWORD)GetStringConfigValue->AddBuffer((unsigned char*)result,
                                                             sizeof(WCHAR) * ((unsigned int)u16_strlen(result) + 1));

    GetStringConfigValue->Add(nameIndex, resultIndex);
    DEBUG_REC(dmpGetStringConfigValue(nameIndex, resultIndex));
}

void MethodContext::dmpGetStringConfigValue(DWORD nameIndex, DWORD resultIndex)
{
    std::string name   = ConvertToUtf8((const WCHAR*)GetStringConfigValue->GetBuffer(nameIndex));
    std::string result = ConvertToUtf8((const WCHAR*)GetStringConfigValue->GetBuffer(resultIndex));
    printf("GetStringConfigValue name %s, result %s", name.c_str(), result.c_str());
    GetStringConfigValue->Unlock();
}

const WCHAR* MethodContext::repGetStringConfigValue(const WCHAR* name)
{
    if (ignoreStoredConfig)
        return nullptr;

    if (GetStringConfigValue == nullptr)
        return nullptr;

    AssertCodeMsg(name != nullptr, EXCEPTIONCODE_MC, "Name can not be nullptr");

    size_t nameLenInBytes = sizeof(WCHAR) * (u16_strlen(name) + 1);
    int    nameIndex      = GetStringConfigValue->Contains((unsigned char*)name, (unsigned int)nameLenInBytes);
    if (nameIndex == -1) // config name not in map
        return nullptr;

    int resultIndex = LookupByKeyOrMissNoMessage(GetStringConfigValue, nameIndex);

    const WCHAR* value = (const WCHAR*)GetStringConfigValue->GetBuffer(resultIndex);

    DEBUG_REP(dmpGetStringConfigValue(nameIndex, (DWORD)resultIndex));
    return value;
}

void MethodContext::dmpSigInstHandleMap(DWORD key, DWORDLONG value)
{
    printf("SigInstHandleMap key %u, value %016" PRIX64 "", key, value);
}

int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len, bool ignoreMethodName /* = false */, CORINFO_METHOD_INFO* optInfo /* = nullptr */, unsigned optFlags /* = 0 */)
{
    if (len < METHOD_IDENTITY_INFO_SIZE)
        return -1;

    // Obtain the Method Info structure for this method
    CORINFO_METHOD_INFO  info;
    CORINFO_METHOD_INFO* pInfo = nullptr;
    unsigned             flags = 0;

    if (optInfo != nullptr)
    {
        // Use the info we've already retrieved from repCompileMethod().
        pInfo = optInfo;
        flags = optFlags;
    }
    else
    {
        CORINFO_OS os;
        repCompileMethod(&info, &flags, &os);
        pInfo = &info;
    }

    // Obtain the jit flags and ISA flags.
    CORJIT_FLAGS corJitFlags;
    repGetJitFlags(&corJitFlags, sizeof(corJitFlags));

    char* obuff = buff;

    // Add the Method Signature. Be careful about potentially huge method signatures; truncate if necessary.
    const char* methodFullName = CallUtils::GetMethodFullName(this, pInfo->ftn, pInfo->args, ignoreMethodName);
    int t = _snprintf_s(buff, len - METHOD_IDENTITY_INFO_NON_NAME_RESERVE, _TRUNCATE, "%s -- ", methodFullName);
    if (t == -1)
    {
        // We truncated the name string, meaning we wrote exactly `len - METHOD_IDENTITY_INFO_NON_NAME_RESERVE` characters
        // (including the terminating null). We advance the buffer pointer by this amount, not including that terminating null.
        t = len - METHOD_IDENTITY_INFO_NON_NAME_RESERVE - 1;
    }
    buff += t;
    len -= t;

    // Add Calling convention information, CorInfoOptions, CorInfoRegionKind, jit flags, and ISA flags.
    t = sprintf_s(buff, len, "CallingConvention: %d, CorInfoOptions: %d, CorInfoRegionKind: %d, JitFlags %016" PRIx64 ", ISA Flags", pInfo->args.callConv,
                  pInfo->options, pInfo->regionKind, corJitFlags.GetFlagsRaw());
    buff += t;
    len -= t;

    uint64_t *raw = corJitFlags.GetInstructionSetFlagsRaw();
    const int flagsFieldCount = corJitFlags.GetInstructionFlagsFieldCount();
    for (int i = flagsFieldCount - 1; i >= 0; i--)
    {
        t = sprintf_s(buff, len, " %016" PRIX64 "", raw[i]);
        buff += t;
        len -= t;
    }

    // Hash the IL Code for this method and append it to the ID info
    char ilHash[MM3_HASH_BUFFER_SIZE];
    dumpHashToBuffer(pInfo->ILCode, pInfo->ILCodeSize, ilHash, MM3_HASH_BUFFER_SIZE);
    t = sprintf_s(buff, len, " ILCode Hash: %s", ilHash);
    buff += t;
    len -= t;

    // Fingerprint the root method PGO data (if any) and append it to the ID info.
    //
    if ((GetPgoInstrumentationResults != nullptr) &&
        (GetPgoInstrumentationResults->GetIndex(CastHandle(pInfo->ftn)) != -1))
    {
        ICorJitInfo::PgoInstrumentationSchema* schema = nullptr;
        UINT32 schemaCount = 0;
        BYTE* schemaData = nullptr;
        ICorJitInfo::PgoSource pgoSource = ICorJitInfo::PgoSource::Unknown;
        HRESULT pgoHR = repGetPgoInstrumentationResults(pInfo->ftn, &schema, &schemaCount, &schemaData, &pgoSource);

        size_t minOffset = (size_t) ~0;
        size_t maxOffset = 0;
        uint64_t totalCount = 0;

        if (SUCCEEDED(pgoHR))
        {
            // Locate the range of the data.
            //
            for (UINT32 i = 0; i < schemaCount; i++)
            {
                size_t start = schema[i].Offset;
                size_t end;
                switch (schema[i].InstrumentationKind)
                {
                    case ICorJitInfo::PgoInstrumentationKind::BasicBlockIntCount:
                    case ICorJitInfo::PgoInstrumentationKind::EdgeIntCount:
                        totalCount += *(uint32_t*)(schemaData + schema[i].Offset);
                        end = start + 4;
                        break;
                    case ICorJitInfo::PgoInstrumentationKind::BasicBlockLongCount:
                    case ICorJitInfo::PgoInstrumentationKind::EdgeLongCount:
                        totalCount += *(uint64_t*)(schemaData + schema[i].Offset);
                        end = start + 8;
                        break;
                    default:
                        continue;
                }

                if (start < minOffset)
                {
                    minOffset = start;
                }

                if (end > maxOffset)
                {
                    maxOffset = end;
                }
            }

            // Hash the counter values.
            //
            if (minOffset < maxOffset)
            {
                char pgoHash[MM3_HASH_BUFFER_SIZE];
                dumpHashToBuffer(schemaData + minOffset, (int)(maxOffset - minOffset), pgoHash,
                                    MM3_HASH_BUFFER_SIZE);

                t = sprintf_s(buff, len, " Pgo Counters %u, Count %" PRIu64 ", Hash: %s", schemaCount, totalCount, pgoHash);
                buff += t;
                len -= t;
            }
        }
    }

    return (int)(buff - obuff);
}

int MethodContext::dumpMethodHashToBuffer(char* buff, int len, bool ignoreMethodName /* = false */, CORINFO_METHOD_INFO* optInfo /* = nullptr */, unsigned optFlags /* = 0 */)
{
    char bufferIdentityInfo[METHOD_IDENTITY_INFO_SIZE];

    int cbLen = dumpMethodIdentityInfoToBuffer(bufferIdentityInfo, METHOD_IDENTITY_INFO_SIZE, ignoreMethodName, optInfo, optFlags);

    if (cbLen < 0)
        return cbLen;

    cbLen = dumpHashToBuffer((BYTE*)bufferIdentityInfo, cbLen, buff, len);

    return cbLen;
}

int MethodContext::dumpHashToBuffer(BYTE* pBuffer, int bufLen, char* hash, int hashLen)
{
    return Hash::HashBuffer(pBuffer, bufLen, hash, hashLen);
}

bool MethodContext::hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool& hasMethodProfile, bool& hasLikelyClass, bool& hasLikelyMethod, ICorJitInfo::PgoSource& pgoSource)
{
    hasEdgeProfile = false;
    hasClassProfile = false;
    hasMethodProfile = false;
    hasLikelyClass = false;
    hasLikelyMethod = false;

    // Obtain the Method Info structure for this method
    CORINFO_METHOD_INFO  info;
    unsigned             flags = 0;
    CORINFO_OS os;
    repCompileMethod(&info, &flags, &os);

    if ((GetPgoInstrumentationResults != nullptr) &&
        (GetPgoInstrumentationResults->GetIndex(CastHandle(info.ftn)) != -1))
    {
        ICorJitInfo::PgoInstrumentationSchema* schema = nullptr;
        UINT32 schemaCount = 0;
        BYTE* schemaData = nullptr;
        HRESULT pgoHR = repGetPgoInstrumentationResults(info.ftn, &schema, &schemaCount, &schemaData, &pgoSource);

        if (SUCCEEDED(pgoHR))
        {
            for (UINT32 i = 0; i < schemaCount; i++)
            {
                hasEdgeProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::EdgeIntCount);
                hasEdgeProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::EdgeLongCount);
                hasClassProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes);
                hasMethodProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramMethods);
                hasLikelyClass |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass);
                hasLikelyMethod |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyMethod);

                if (hasEdgeProfile && hasClassProfile && hasLikelyClass && hasLikelyMethod)
                {
                    break;
                }
            }

            return true;
        }
    }

    return false;
}

MethodContext::Environment MethodContext::cloneEnvironment()
{
    MethodContext::Environment env;
    if (GetIntConfigValue != nullptr)
    {
        env.getIntConfigValue = new LightWeightMap<Agnostic_ConfigIntInfo, DWORD>(*GetIntConfigValue);
    }
    if (GetStringConfigValue != nullptr)
    {
        env.getStingConfigValue = new LightWeightMap<DWORD, DWORD>(*GetStringConfigValue);
    }
    return env;
}

// Check that there is a difference between the current environment variables maps and the prevEnv.
bool MethodContext::WasEnvironmentChanged(const Environment& prevEnv)
{
    if (!IsEnvironmentHeaderEqual(prevEnv))
    {
        return true;
    }
    if (!IsEnvironmentContentEqual(prevEnv))
    {
        return true;
    }
    return false;
}

// Check that environment maps headers are equal to the prevEnv maps headers.
bool MethodContext::IsEnvironmentHeaderEqual(const Environment& prevEnv)
{
    if (!AreLWMHeadersEqual(prevEnv.getIntConfigValue, GetIntConfigValue))
    {
        return false;
    }
    if (!AreLWMHeadersEqual(prevEnv.getStingConfigValue, GetStringConfigValue))
    {
        return false;
    }
    return true;
}

// Check that environment maps content is equal to the prevEnv content.
bool MethodContext::IsEnvironmentContentEqual(const Environment& prevEnv)
{
    if (!IsIntConfigContentEqual(prevEnv.getIntConfigValue, GetIntConfigValue))
    {
        return false;
    }
    if (!IsStringContentEqual(prevEnv.getStingConfigValue, GetStringConfigValue))
    {
        return false;
    }
    return true;
}

// Check pointers to be both initizlized or null and number of keys to be equal.
template <typename key, typename value>
bool MethodContext::AreLWMHeadersEqual(LightWeightMap<key, value>* prev, LightWeightMap<key, value>* curr)
{
    if (prev == nullptr && curr == nullptr)
    {
        return true;
    }
    if (prev != nullptr && curr != nullptr)
    {
        if (prev->GetCount() == curr->GetCount())
        {
            return true;
        }
    }
    return false;
}

bool MethodContext::IsIntConfigContentEqual(LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* prev,
                                            LightWeightMap<Agnostic_ConfigIntInfo, DWORD>* curr)
{
    if (prev != nullptr && curr != nullptr)
    {
        if (prev->GetCount() != curr->GetCount())
        {
            return false;
        }

        for (unsigned i = 0; i < prev->GetCount(); ++i)
        {
            DWORD currValue = curr->GetItem(i);
            DWORD prevValue = prev->GetItem(i);
            if (currValue != prevValue)
            {
                return false;
            }

            Agnostic_ConfigIntInfo currKey = curr->GetKey(i);
            Agnostic_ConfigIntInfo prevKey = prev->GetKey(i);

            if (currKey.defaultValue != prevKey.defaultValue)
            {
                return false;
            }

            DWORD  currNameIndex = currKey.nameIndex;
            LPCSTR currName      = (LPCSTR)curr->GetBuffer(currNameIndex);
            DWORD  prevNameIndex = prevKey.nameIndex;
            LPCSTR prevName      = (LPCSTR)prev->GetBuffer(currNameIndex);
            if (strcmp(currName, prevName) != 0)
            {
                return false;
            }
        }
        return true;
    }
    else
    {
        return (prev == curr);
    }
}

bool MethodContext::IsStringContentEqual(LightWeightMap<DWORD, DWORD>* prev, LightWeightMap<DWORD, DWORD>* curr)
{
    if (prev != nullptr && curr != nullptr)
    {
        if (prev->GetCount() != curr->GetCount())
        {
            return false;
        }

        for (unsigned i = 0; i < curr->GetCount(); ++i)
        {
            DWORD  currKeyIndex = curr->GetKey(i);
            LPCSTR currKey      = (LPCSTR)curr->GetBuffer(currKeyIndex);
            DWORD  prevKeyIndex = prev->GetKey(i);
            LPCSTR prevKey      = (LPCSTR)prev->GetBuffer(prevKeyIndex);
            if (strcmp(currKey, prevKey) != 0)
            {
                return false;
            }

            DWORD  currValueIndex = curr->GetItem(i);
            LPCSTR currValue      = (LPCSTR)curr->GetBuffer(currValueIndex);
            DWORD  prevValueIndex = prev->GetItem(i);
            LPCSTR prevValue      = (LPCSTR)prev->GetBuffer(prevValueIndex);
            if (strcmp(currValue, prevValue) != 0)
            {
                return false;
            }
        }
        return true;
    }
    else
    {
        return (prev == curr);
    }
}

bool g_debugRec = false;
bool g_debugRep = false;

void SetDebugDumpVariables()
{
    static WCHAR* g_debugRecStr = nullptr;
    static WCHAR* g_debugRepStr = nullptr;
    if (g_debugRecStr == nullptr)
    {
        g_debugRecStr = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimDebugRec"), W("0"));
    }
    if (g_debugRepStr == nullptr)
    {
        g_debugRepStr = GetEnvironmentVariableWithDefaultW(W("SuperPMIShimDebugRep"), W("0"));
    }

    if (0 == u16_strcmp(g_debugRecStr, W("1")))
    {
        g_debugRec = true;
    }
    if (0 == u16_strcmp(g_debugRepStr, W("1")))
    {
        g_debugRep = true;
    }
}
